#define SQLITE_MAX_EXPR_DEPTH

#if !SQLITE_MAX_VARIABLE_NUMBER
using System;
using System.Diagnostics;
using Community.CsharpSqlite.Entity;
using ynVar = System.Int16;

#else
using ynVar = System.Int32; 
#endif

namespace Community.CsharpSqlite
{
    public  class ExprHelper
    {
        /*
    ** 2001 September 15
    **
    ** The author disclaims copyright to this source code.  In place of
    ** a legal notice, here is a blessing:
    **
    **    May you do good and not evil.
    **    May you find forgiveness for yourself and forgive others.
    **    May you share freely, never taking more than you give.
    **
    *************************************************************************
    ** This file contains routines used for analyzing expressions and
    ** for generating VDBE code that evaluates expressions in SQLite.
    *************************************************************************
    **  Included in SQLite3 port to C#-SQLite;  2008 Noah B Hart
    **  C#-SQLite is an independent reimplementation of the SQLite software library
    **
    **  SQLITE_SOURCE_ID: 2010-03-09 19:31:43 4ae453ea7be69018d8c16eb8dabe05617397dc4d
    **
    **  $Header: Community.CsharpSqlite/src/expr_c.cs,v 6604176a7dbe 2010/03/12 23:35:36 Noah $
    *************************************************************************
    */
        //#include "sqliteInt.h"

        /*
    ** Return the 'affinity' of the expression pExpr if any.
    **
    ** If pExpr is a column, a reference to a column via an 'AS' alias,
    ** or a sub-select with a column as the return value, then the
    ** affinity of that column is returned. Otherwise, 0x00 is returned,
    ** indicating no affinity for the expression.
    **
    ** i.e. the WHERE clause expresssions in the following statements all
    ** have an affinity:
    **
    ** CREATE TABLE t1(a);
    ** SELECT * FROM t1 WHERE a;
    ** SELECT a AS b FROM t1 WHERE b;
    ** SELECT * FROM t1 WHERE (select a from t1);
    */

        public static char ExprAffinity(Expr pExpr)
        {
            int op = pExpr.op;
            if (op == TokenKeyword.TK_SELECT)
            {
                Debug.Assert((pExpr.flags & ExprFlag.EP_xIsSelect) != 0);
                return ExprAffinity(pExpr.x.pSelect.pEList.a[0].pExpr);
            }
#if !SQLITE_OMIT_CAST
            if (op == TokenKeyword.TK_CAST)
            {
                Debug.Assert(!Helper.ExprHasProperty(pExpr, ExprFlag.EP_IntValue));
                return Build.AffinityType(pExpr.u.zToken);
            }
#endif
            if ((op == TokenKeyword.TK_AGG_COLUMN || op == TokenKeyword.TK_COLUMN || op == TokenKeyword.TK_REGISTER)
                && pExpr.pTab != null
                )
            {
                /* op==TokenKeyword.TK_REGISTER && pExpr.pTab!=0 happens when pExpr was originally
        ** a TokenKeyword.TK_COLUMN but was previously evaluated and cached in a register */
                int j = pExpr.iColumn;
                if (j < 0) return ColumnAffinityType.SQLITE_AFF_INTEGER;
                Debug.Assert(pExpr.pTab != null && j < pExpr.pTab.nCol);
                return pExpr.pTab.aCol[j].affinity;
            }
            return pExpr.affinity;
        }

        /*
    ** Set the collating sequence for expression pExpr to be the collating
    ** sequence named by pToken.   Return a pointer to the revised expression.
    ** The collating sequence is marked as "explicit" using the ExprFlag.EP_ExpCollate
    ** flag.  An explicit collating sequence will override implicit
    ** collating sequences.
    */

        public static Expr ExprSetColl(Parse pParse, Expr pExpr, Token pCollName)
        {
            string zColl; /* Dequoted name of collation sequence */
            CollSeq pColl;
            sqlite3 db = pParse.db;
            zColl = Build.NameFromToken(db, pCollName);
            if (pExpr != null && zColl != null)
            {
                pColl = Build.LocateCollSeq(pParse, zColl);
                if (pColl != null)
                {
                    pExpr.pColl = pColl;
                    pExpr.flags |= ExprFlag.EP_ExpCollate;
                }
            }
            MemPool.DbFree(db, ref zColl);
            return pExpr;
        }

        /*
    ** Return the default collation sequence for the expression pExpr. If
    ** there is no default collation type, return 0.
    */

        public static CollSeq ExprCollSeq(Parse pParse, Expr pExpr)
        {
            CollSeq pColl = null;
            Expr p = pExpr;
            while (UnitTest.ALWAYS(p != null))
            {
                int op;
                pColl = pExpr.pColl;
                if (pColl != null) break;
                op = p.op;
                if (p.pTab != null && (
                                          op == TokenKeyword.TK_AGG_COLUMN || op == TokenKeyword.TK_COLUMN || op == TokenKeyword.TK_REGISTER ||
                                          op == TokenKeyword.TK_TRIGGER
                                      ))
                {
                    /* op==TokenKeyword.TK_REGISTER && p->pTab!=0 happens when pExpr was originally
          ** a TokenKeyword.TK_COLUMN but was previously evaluated and cached in a register */
                    string zColl;
                    int j = p.iColumn;
                    if (j >= 0)
                    {
                        sqlite3 db = pParse.db;
                        zColl = p.pTab.aCol[j].zColl;
                        pColl = Callback.FindCollSeq(db, Helper.ENC(db), zColl, 0);
                        pExpr.pColl = pColl;
                    }
                    break;
                }
                if (op != TokenKeyword.TK_CAST && op != TokenKeyword.TK_UPLUS)
                {
                    break;
                }
                p = p.pLeft;
            }
            if (Callback.CheckCollSeq(pParse, pColl) != 0)
            {
                pColl = null;
            }
            return pColl;
        }

        /*
    ** pExpr is an operand of a comparison operator.  aff2 is the
    ** type affinity of the other operand.  This routine returns the
    ** type affinity that should be used for the comparison operator.
    */

        public static char CompareAffinity(Expr pExpr, char aff2)
        {
            char aff1 = ExprAffinity(pExpr);
            if (aff1 != '\0' && aff2 != '\0')
            {
                /* Both sides of the comparison are columns. If one has numeric
        ** affinity, use that. Otherwise use no affinity.
        */
                if (aff1 >= ColumnAffinityType.SQLITE_AFF_NUMERIC || aff2 >= ColumnAffinityType.SQLITE_AFF_NUMERIC)
                    //        if (sqlite3IsNumericAffinity(aff1) || sqlite3IsNumericAffinity(aff2))
                {
                    return ColumnAffinityType.SQLITE_AFF_NUMERIC;
                }
                else
                {
                    return ColumnAffinityType.SQLITE_AFF_NONE;
                }
            }
            else if (aff1 == '\0' && aff2 == '\0')
            {
                /* Neither side of the comparison is a column.  Compare the
        ** results directly.
        */
                return ColumnAffinityType.SQLITE_AFF_NONE;
            }
            else
            {
                /* One side is a column, the other is not. Use the columns affinity. */
                Debug.Assert(aff1 == 0 || aff2 == 0);
                return (aff1 != '\0' ? aff1 : aff2);
            }
        }

        /*
    ** pExpr is a comparison operator.  Return the type affinity that should
    ** be applied to both operands prior to doing the comparison.
    */

        private static char comparisonAffinity(Expr pExpr)
        {
            char aff;
            Debug.Assert(pExpr.op == TokenKeyword.TK_EQ || pExpr.op == TokenKeyword.TK_IN || pExpr.op == TokenKeyword.TK_LT ||
                         pExpr.op == TokenKeyword.TK_GT || pExpr.op == TokenKeyword.TK_GE || pExpr.op == TokenKeyword.TK_LE ||
                         pExpr.op == TokenKeyword.TK_NE || pExpr.op == TokenKeyword.TK_IS || pExpr.op == TokenKeyword.TK_ISNOT);
            Debug.Assert(pExpr.pLeft != null);
            aff = ExprAffinity(pExpr.pLeft);
            if (pExpr.pRight != null)
            {
                aff = CompareAffinity(pExpr.pRight, aff);
            }
            else if (Helper.ExprHasProperty(pExpr, ExprFlag.EP_xIsSelect))
            {
                aff = CompareAffinity(pExpr.x.pSelect.pEList.a[0].pExpr, aff);
            }
            else if (aff == '\0')
            {
                aff = ColumnAffinityType.SQLITE_AFF_NONE;
            }
            return aff;
        }

        /*
    ** pExpr is a comparison expression, eg. '=', '<', IN(...) etc.
    ** idx_affinity is the affinity of an indexed column. Return true
    ** if the index with affinity idx_affinity may be used to implement
    ** the comparison in pExpr.
    */

        public static bool IndexAffinityOk(Expr pExpr, char idx_affinity)
        {
            char aff = comparisonAffinity(pExpr);
            switch (aff)
            {
                case ColumnAffinityType.SQLITE_AFF_NONE:
                    return true;
                case ColumnAffinityType.SQLITE_AFF_TEXT:
                    return idx_affinity == ColumnAffinityType.SQLITE_AFF_TEXT;
                default:
                    return idx_affinity >= ColumnAffinityType.SQLITE_AFF_NUMERIC; // sqlite3IsNumericAffinity(idx_affinity);
            }
        }

        /*
    ** Return the P5 value that should be used for a binary comparison
    ** opcode (OPCode.OP_Eq, OPCode.OP_Ge etc.) used to compare pExpr1 and pExpr2.
    */

        private static byte binaryCompareP5(Expr pExpr1, Expr pExpr2, int jumpIfNull)
        {
            var aff = (byte) ExprAffinity(pExpr2);
            aff = (byte) ((byte) CompareAffinity(pExpr1, (char) aff) | (byte) jumpIfNull);
            return aff;
        }

        /*
    ** Return a pointer to the collation sequence that should be used by
    ** a binary comparison operator comparing pLeft and pRight.
    **
    ** If the left hand expression has a collating sequence type, then it is
    ** used. Otherwise the collation sequence for the right hand expression
    ** is used, or the default (BINARY) if neither expression has a collating
    ** type.
    **
    ** Argument pRight (but not pLeft) may be a null pointer. In this case,
    ** it is not considered.
    */

        public static CollSeq BinaryCompareCollSeq(
            Parse pParse,
            Expr pLeft,
            Expr pRight
            )
        {
            CollSeq pColl;
            Debug.Assert(pLeft != null);
            if ((pLeft.flags & ExprFlag.EP_ExpCollate) != 0)
            {
                Debug.Assert(pLeft.pColl != null);
                pColl = pLeft.pColl;
            }
            else if (pRight != null && ((pRight.flags & ExprFlag.EP_ExpCollate) != 0))
            {
                Debug.Assert(pRight.pColl != null);
                pColl = pRight.pColl;
            }
            else
            {
                pColl = ExprCollSeq(pParse, pLeft);
                if (pColl == null)
                {
                    pColl = ExprCollSeq(pParse, pRight);
                }
            }
            return pColl;
        }

        /*
    ** Generate code for a comparison operator.
    */

        private static int codeCompare(
            Parse pParse, /* The parsing (and code generating) context */
            Expr pLeft, /* The left operand */
            Expr pRight, /* The right operand */
            int opcode, /* The comparison opcode */
            int in1, int in2, /* Register holding operands */
            int dest, /* Jump here if true.  */
            int jumpIfNull /* If true, jump if either operand is NULL */
            )
        {
            int p5;
            int addr;
            CollSeq p4;

            p4 = BinaryCompareCollSeq(pParse, pLeft, pRight);
            p5 = binaryCompareP5(pLeft, pRight, jumpIfNull);
            addr = VdbeAux.VdbeAddOp4(pParse.pVdbe, opcode, in2, dest, in1,
                                     p4, P4Type.P4_COLLSEQ);
            VdbeAux.VdbeChangeP5(pParse.pVdbe, (byte) p5);
            return addr;
        }

#if SQLITE_MAX_EXPR_DEPTH
        //>0
        /*
** Check that argument nHeight is less than or equal to the maximum
** expression depth allowed. If it is not, leave an error message in
** pParse.
*/

        public static int ExprCheckHeight(Parse pParse, int nHeight)
        {
            int rc = StatusCode.SQLITE_OK;
            int mxHeight = pParse.db.aLimit[LimitCategory.SQLITE_LIMIT_EXPR_DEPTH];
            if (nHeight > mxHeight)
            {
                Utility.Sqlite3ErrorMsg(pParse,
                                "Expression tree is too large (maximum depth %d)", mxHeight
                    );
                rc = StatusCode.SQLITE_ERROR;
            }
            return rc;
        }

        /* The following three functions, heightOfExpr(), heightOfExprList()
    ** and heightOfSelect(), are used to determine the maximum height
    ** of any expression tree referenced by the structure passed as the
    ** first argument.
    **
    ** If this maximum height is greater than the current value pointed
    ** to by pnHeight, the second parameter, then set pnHeight to that
    ** value.
    */

        private static void heightOfExpr(Expr p, ref int pnHeight)
        {
            if (p != null)
            {
                if (p.nHeight > pnHeight)
                {
                    pnHeight = p.nHeight;
                }
            }
        }

        private static void heightOfExprList(ExprList p, ref int pnHeight)
        {
            if (p != null)
            {
                int i;
                for (i = 0; i < p.nExpr; i++)
                {
                    heightOfExpr(p.a[i].pExpr, ref pnHeight);
                }
            }
        }

        private static void heightOfSelect(Select p, ref int pnHeight)
        {
            if (p != null)
            {
                heightOfExpr(p.pWhere, ref pnHeight);
                heightOfExpr(p.pHaving, ref pnHeight);
                heightOfExpr(p.pLimit, ref pnHeight);
                heightOfExpr(p.pOffset, ref pnHeight);
                heightOfExprList(p.pEList, ref pnHeight);
                heightOfExprList(p.pGroupBy, ref pnHeight);
                heightOfExprList(p.pOrderBy, ref pnHeight);
                heightOfSelect(p.pPrior, ref pnHeight);
            }
        }

        /*
    ** Set the Expr.nHeight variable in the structure passed as an
    ** argument. An expression with no children, Expr.x.pList or
    ** Expr.x.pSelect member has a height of 1. Any other expression
    ** has a height equal to the maximum height of any other
    ** referenced Expr plus one.
    */

        private static void exprSetHeight(Expr p)
        {
            int nHeight = 0;
            heightOfExpr(p.pLeft, ref nHeight);
            heightOfExpr(p.pRight, ref nHeight);
            if (Helper.ExprHasProperty(p, ExprFlag.EP_xIsSelect))
            {
                heightOfSelect(p.x.pSelect, ref nHeight);
            }
            else
            {
                heightOfExprList(p.x.pList, ref nHeight);
            }
            p.nHeight = nHeight + 1;
        }

        /*
    ** Set the Expr.nHeight variable using the exprSetHeight() function. If
    ** the height is greater than the maximum allowed expression depth,
    ** leave an error in pParse.
    */

        public static void ExprSetHeight(Parse pParse, Expr p)
        {
            exprSetHeight(p);
            ExprCheckHeight(pParse, p.nHeight);
        }

        /*
    ** Return the maximum height of any expression tree referenced
    ** by the select statement passed as an argument.
    */

        public static int SelectExprHeight(Select p)
        {
            int nHeight = 0;
            heightOfSelect(p, ref nHeight);
            return nHeight;
        }
#else
//#define exprSetHeight(y)
#endif
        //* SqliteLimit.SQLITE_MAX_EXPR_DEPTH>0 */

        /*
** This routine is the core allocator for Expr nodes.
**
** Construct a new expression node and return a pointer to it.  Memory
** for this node and for the pToken argument is a single allocation
** obtained from sqlite3DbMalloc().  The calling function
** is responsible for making sure the node eventually gets freed.
**
** If dequote is true, then the token (if it exists) is dequoted.
** If dequote is false, no dequoting is performance.  The deQuote
** parameter is ignored if pToken is NULL or if the token does not
** appear to be quoted.  If the quotes were of the form "..." (double-quotes)
** then the ExprFlag.EP_DblQuoted flag is set on the expression node.
**
** Special case:  If op==TokenKeyword.TK_INTEGER and pToken points to a string that
** can be translated into a 32-bit integer, then the token is not
** stored in u.zToken.  Instead, the integer values is written
** into u.iValue and the ExprFlag.EP_IntValue flag is set.  No extra storage
** is allocated to hold the integer text and the dequote flag is ignored.
*/

        public static Expr ExprAlloc(
            sqlite3 db, /* Handle for Malloc.DbMallocZero() (may be null) */
            int op, /* Expression opcode */
            Token pToken, /* Token argument.  Might be NULL */
            int dequote /* True to dequote */
            )
        {
            Expr pNew;
            int nExtra = 0;
            int iValue = 0;

            if (pToken != null)
            {
                if (op != TokenKeyword.TK_INTEGER || pToken.z == null || pToken.z.Length == 0
                    || Utility.Sqlite3GetInt32(pToken.z, ref iValue) == false)
                {
                    nExtra = pToken.n + 1;
                }
            }
            pNew = new Expr(); //Malloc.DbMallocZero(db, sizeof(Expr)+nExtra);
            if (pNew != null)
            {
                pNew.op = (byte) op;
                pNew.iAgg = -1;
                if (pToken != null)
                {
                    if (nExtra == 0)
                    {
                        pNew.flags |= ExprFlag.EP_IntValue;
                        pNew.u.iValue = iValue;
                    }
                    else
                    {
                        int c;
                        //pNew.u.zToken = (char*)&pNew[1];
                        if (pToken.n > 0)
                            pNew.u.zToken = pToken.z.Substring(0, pToken.n);
                                //memcpy(pNew.u.zToken, pToken.z, pToken.n);
                        else if (pToken.n == 0 && pToken.z == "") pNew.u.zToken = "";
                        //pNew.u.zToken[pToken.n] = 0;
                        if (dequote != 0 && nExtra >= 3
                            && ((c = pToken.z[0]) == '\'' || c == '"' || c == '[' || c == '`'))
                        {
#if DEBUG_CLASS_EXPR || DEBUG_CLASS_ALL
Utility.Sqlite3Dequote(ref pNew.u._zToken);
#else
                            Utility.Sqlite3Dequote(ref pNew.u.zToken);
#endif
                            if (c == '"') pNew.flags |= ExprFlag.EP_DblQuoted;
                        }
                    }
                }
#if SQLITE_MAX_EXPR_DEPTH
//>0
                pNew.nHeight = 1;
#endif
            }
            return pNew;
        }

        /*
    ** Allocate a new expression node from a zero-terminated token that has
    ** already been dequoted.
    */

        public static Expr NewExpr(
            sqlite3 db, /* Handle for Malloc.DbMallocZero() (may be null) */
            int op, /* Expression opcode */
            string zToken /* Token argument.  Might be NULL */
            )
        {
            var x = new Token();
            x.z = zToken;
            x.n = !String.IsNullOrEmpty(zToken) ? Utility.Sqlite3Strlen30(zToken) : 0;
            return ExprAlloc(db, op, x, 0);
        }

        /*
    ** Attach subtrees pLeft and pRight to the Expr node pRoot.
    **
    ** If pRoot==NULL that means that a memory allocation error has occurred.
    ** In that case, delete the subtrees pLeft and pRight.
    */

        public static void ExprAttachSubtrees(
            sqlite3 db,
            Expr pRoot,
            Expr pLeft,
            Expr pRight
            )
        {
            if (pRoot == null)
            {
                //Debug.Assert( db.mallocFailed != 0 );
                ExprDelete(db, ref pLeft);
                ExprDelete(db, ref pRight);
            }
            else
            {
                if (pRight != null)
                {
                    pRoot.pRight = pRight;
                    if ((pRight.flags & ExprFlag.EP_ExpCollate) != 0)
                    {
                        pRoot.flags |= ExprFlag.EP_ExpCollate;
                        pRoot.pColl = pRight.pColl;
                    }
                }
                if (pLeft != null)
                {
                    pRoot.pLeft = pLeft;
                    if ((pLeft.flags & ExprFlag.EP_ExpCollate) != 0)
                    {
                        pRoot.flags |= ExprFlag.EP_ExpCollate;
                        pRoot.pColl = pLeft.pColl;
                    }
                }
                exprSetHeight(pRoot);
            }
        }

        /*
    ** Allocate a Expr node which joins as many as two subtrees.
    **
    ** One or both of the subtrees can be NULL.  Return a pointer to the new
    ** Expr node.  Or, if an OOM error occurs, set pParse->db->mallocFailed,
    ** free the subtrees and return NULL.
    */
        // OVERLOADS, so I don't need to rewrite parse.c
        public static Expr PExpr(Parse pParse, int op, int null_3, int null_4, int null_5)
        {
            return PExpr(pParse, op, null, null, null);
        }

        public static Expr PExpr(Parse pParse, int op, int null_3, int null_4, Token pToken)
        {
            return PExpr(pParse, op, null, null, pToken);
        }

        public static Expr PExpr(Parse pParse, int op, Expr pLeft, int null_4, int null_5)
        {
            return PExpr(pParse, op, pLeft, null, null);
        }

        public static Expr PExpr(Parse pParse, int op, Expr pLeft, int null_4, Token pToken)
        {
            return PExpr(pParse, op, pLeft, null, pToken);
        }

        public static Expr PExpr(Parse pParse, int op, Expr pLeft, Expr pRight, int null_5)
        {
            return PExpr(pParse, op, pLeft, pRight, null);
        }

        public static Expr PExpr(
            Parse pParse, /* Parsing context */
            int op, /* Expression opcode */
            Expr pLeft, /* Left operand */
            Expr pRight, /* Right operand */
            Token pToken /* Argument Token */
            )
        {
            Expr p = ExprAlloc(pParse.db, op, pToken, 1);
            ExprAttachSubtrees(pParse.db, p, pLeft, pRight);
            return p;
        }


        /*
    ** When doing a nested parse, you can include terms in an expression
    ** that look like this:   #1 #2 ...  These terms refer to registers
    ** in the virtual machine.  #N is the N-th register.
    **
    ** This routine is called by the parser to deal with on of those terms.
    ** It immediately generates code to store the value in a memory location.
    ** The returns an expression that will code to extract the value from
    ** that memory location as needed.
    */

        public static Expr RegisterExpr(Parse pParse, Token pToken)
        {
            Vdbe v = pParse.pVdbe;
            Expr p;
            if (pParse.nested == 0)
            {
                Utility.Sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", pToken);
                return ExprHelper.PExpr(pParse, TokenKeyword.TK_NULL, null, null, null);
            }
            if (v == null) return null;
            p = ExprHelper.PExpr(pParse, TokenKeyword.TK_REGISTER, null, null, pToken);
            if (p == null)
            {
                return null; /* Malloc failed */
            }
            p.u.iValue = Custom.Atoi(pToken.z.Substring(1));
            ; //Custom.Atoi((char*)&pToken - z[1]);
            return p;
        }

        /*
    ** Join two expressions using an AND operator.  If either expression is
    ** NULL, then just return the other expression.
    */

        public static Expr ExprAnd(sqlite3 db, Expr pLeft, Expr pRight)
        {
            if (pLeft == null)
            {
                return pRight;
            }
            else if (pRight == null)
            {
                return pLeft;
            }
            else
            {
                Expr pNew = ExprAlloc(db, TokenKeyword.TK_AND, null, 0);
                ExprAttachSubtrees(db, pNew, pLeft, pRight);
                return pNew;
            }
        }

        /*
    ** Construct a new expression node for a function with multiple
    ** arguments.
    */
        // OVERLOADS, so I don't need to rewrite parse.c
        public static Expr ExprFunction(Parse pParse, int null_2, Token pToken)
        {
            return ExprFunction(pParse, null, pToken);
        }

        public static Expr ExprFunction(Parse pParse, ExprList pList, int null_3)
        {
            return ExprFunction(pParse, pList, null);
        }

        public static Expr ExprFunction(Parse pParse, ExprList pList, Token pToken)
        {
            Expr pNew;
            sqlite3 db = pParse.db;
            Debug.Assert(pToken != null);
            pNew = ExprAlloc(db, TokenKeyword.TK_FUNCTION, pToken, 1);
            if (pNew == null)
            {
                ExprListDelete(db, ref pList); /* Avoid memory leak when malloc fails */
                return null;
            }
            pNew.x.pList = pList;
            Debug.Assert(!Helper.ExprHasProperty(pNew, ExprFlag.EP_xIsSelect));

            ExprSetHeight(pParse, pNew);
            return pNew;
        }

        /*
    ** Assign a variable number to an expression that encodes a wildcard
    ** in the original SQL statement.
    **
    ** Wildcards consisting of a single "?" are assigned the next sequential
    ** variable number.
    **
    ** Wildcards of the form "?nnn" are assigned the number "nnn".  We make
    ** sure "nnn" is not too be to avoid a denial of service attack when
    ** the SQL statement comes from an external source.
    **
    ** Wildcards of the form ":aaa", "@aaa" or "$aaa" are assigned the same number
    ** as the previous instance of the same wildcard.  Or if this is the first
    ** instance of the wildcard, the next sequenial variable number is
    ** assigned.
    */

        public static void ExprAssignVarNumber(Parse pParse, Expr pExpr)
        {
            sqlite3 db = pParse.db;
            string z;

            if (pExpr == null) return;
            Debug.Assert(!Helper.ExprHasAnyProperty(pExpr, ExprFlag.EP_IntValue | ExprFlag.EP_TokenOnly | ExprFlag.EP_Reduced));
            z = pExpr.u.zToken;
            Debug.Assert(z != null);
            Debug.Assert(z.Length != 0);
            if (z.Length == 1)
            {
                /* Wildcard of the form "?".  Assign the next variable number */
                Debug.Assert(z[0] == '?');
                pExpr.iColumn = (ynVar) (++pParse.nVar);
            }
            else if (z[0] == '?')
            {
                /* Wildcard of the form "?nnn".  Convert "nnn" to an integer and
        ** use it as the variable number */
                int i = Custom.Atoi(z.Substring(1)); //Custom.Atoi((char*)&z[1]);
                pExpr.iColumn = (ynVar) i;
                UnitTest.TestCase(i == 0);
                UnitTest.TestCase(i == 1);
                UnitTest.TestCase(i == db.aLimit[LimitCategory.SQLITE_LIMIT_VARIABLE_NUMBER] - 1);
                UnitTest.TestCase(i == db.aLimit[LimitCategory.SQLITE_LIMIT_VARIABLE_NUMBER]);
                if (i < 1 || i > db.aLimit[LimitCategory.SQLITE_LIMIT_VARIABLE_NUMBER])
                {
                    Utility.Sqlite3ErrorMsg(pParse, "variable number must be between ?1 and ?%d",
                                    db.aLimit[LimitCategory.SQLITE_LIMIT_VARIABLE_NUMBER]);
                }
                if (i > pParse.nVar)
                {
                    pParse.nVar = i;
                }
            }
            else
            {
                /* Wildcards like ":aaa", "$aaa" or "@aaa".  Reuse the same variable
        ** number as the prior appearance of the same name, or if the name
        ** has never appeared before, reuse the same variable number
        */
                int i;
                int n;
                n = Utility.Sqlite3Strlen30(z);
                for (i = 0; i < pParse.nVarExpr; i++)
                {
                    Expr pE = pParse.apVarExpr[i];
                    Debug.Assert(pE != null);
                    if (Custom.Memcmp(pE.u.zToken, z, n) == 0 && pE.u.zToken.Length == n)
                    {
                        pExpr.iColumn = pE.iColumn;
                        break;
                    }
                }
                if (i >= pParse.nVarExpr)
                {
                    pExpr.iColumn = (ynVar) (++pParse.nVar);
                    if (pParse.nVarExpr >= pParse.nVarExprAlloc - 1)
                    {
                        int oldLength = pParse.nVarExprAlloc;
                        pParse.nVarExprAlloc += pParse.nVarExprAlloc + 10;

                        var newXper = new Expr[pParse.nVarExprAlloc];
                        for (int iter = 0; iter < newXper.Length && iter < oldLength; iter++)
                        {
                            newXper[iter] = pParse.apVarExpr[iter];
                        }
                        pParse.apVarExpr = newXper;
                        //Malloc.DbReallocOrFree(
                        //  db,
                        //  pParse.apVarExpr,
                        //  pParse.nVarExprAlloc*sizeof(pParse.apVarExpr[0])
                        //);
                    }
                    //if ( 0 == db.mallocFailed )
                    {
                        Debug.Assert(pParse.apVarExpr != null);
                        pParse.apVarExpr[pParse.nVarExpr++] = pExpr;
                    }
                }
            }
            if (pParse.nErr == 0 && pParse.nVar > db.aLimit[LimitCategory.SQLITE_LIMIT_VARIABLE_NUMBER])
            {
                Utility.Sqlite3ErrorMsg(pParse, "too many SQL variables");
            }
        }

        /*
    ** Recursively delete an expression tree.
    */

        public static void ExprDelete(sqlite3 db, ref Expr p)
        {
            if (p == null) return;
            if (!Helper.ExprHasAnyProperty(p, ExprFlag.EP_TokenOnly))
            {
                ExprDelete(db, ref p.pLeft);
                ExprDelete(db, ref p.pRight);
                if (!Helper.ExprHasProperty(p, ExprFlag.EP_Reduced) && (p.flags2 & Const.EP2_MallocedToken) != 0)
                {
#if DEBUG_CLASS_EXPR || DEBUG_CLASS_ALL
MemPool.DbFree( db, ref p.u._zToken );
#else
                    MemPool.DbFree(db, ref p.u.zToken);
#endif
                }
                if (Helper.ExprHasProperty(p, ExprFlag.EP_xIsSelect))
                {
                    sqlite3SelectDelete(db, ref p.x.pSelect);
                }
                else
                {
                    ExprListDelete(db, ref p.x.pList);
                }
            }
            if (!Helper.ExprHasProperty(p, ExprFlag.EP_Static))
            {
                MemPool.DbFree(db, ref p);
            }
        }

        /*
    ** Return the number of bytes allocated for the expression structure
    ** passed as the first argument. This is always one of Const.EXPR_FULLSIZE,
    ** Const.EXPR_REDUCEDSIZE or Const.EXPR_TOKENONLYSIZE.
    */

        private static int exprStructSize(Expr p)
        {
            if (Helper.ExprHasProperty(p, ExprFlag.EP_TokenOnly)) return Const.EXPR_TOKENONLYSIZE;
            if (Helper.ExprHasProperty(p, ExprFlag.EP_Reduced)) return Const.EXPR_REDUCEDSIZE;
            return Const.EXPR_FULLSIZE;
        }

        /*
    ** The dupedExpr*Size() routines each return the number of bytes required
    ** to store a copy of an expression or expression tree.  They differ in
    ** how much of the tree is measured.
    **
    **     dupedExprStructSize()     Size of only the Expr structure
    **     dupedExprNodeSize()       Size of Expr + space for token
    **     dupedExprSize()           Expr + token + subtree components
    **
    ***************************************************************************
    **
    ** The dupedExprStructSize() function returns two values OR-ed together:
    ** (1) the space required for a copy of the Expr structure only and
    ** (2) the EP_xxx flags that indicate what the structure size should be.
    ** The return values is always one of:
    **
    **      Const.EXPR_FULLSIZE
    **      Const.EXPR_REDUCEDSIZE   | EP_Reduced
    **      Const.EXPR_TOKENONLYSIZE | ExprFlag.EP_TokenOnly
    **
    ** The size of the structure can be found by masking the return value
    ** of this routine with 0xfff.  The flags can be found by masking the
    ** return value with EP_Reduced|EP_TokenOnly.
    **
    ** Note that with flags==Const.EXPRDUP_REDUCE, this routines works on full-size
    ** (unreduced) Expr objects as they or originally constructed by the parser.
    ** During expression analysis, extra information is computed and moved into
    ** later parts of teh Expr object and that extra information might get chopped
    ** off if the expression is reduced.  Note also that it does not work to
    ** make a Const.EXPRDUP_REDUCE copy of a reduced expression.  It is only legal
    ** to reduce a pristine expression tree from the parser.  The implementation
    ** of dupedExprStructSize() contain multiple Debug.Assert() statements that attempt
    ** to enforce this constraint.
    */

        private static int dupedExprStructSize(Expr p, int flags)
        {
            int nSize;
            Debug.Assert(flags == Const.EXPRDUP_REDUCE || flags == 0); /* Only one flag value allowed */
            if (0 == (flags & Const.EXPRDUP_REDUCE))
            {
                nSize = Const.EXPR_FULLSIZE;
            }
            else
            {
                Debug.Assert(!Helper.ExprHasAnyProperty(p, ExprFlag.EP_TokenOnly | ExprFlag.EP_Reduced));
                Debug.Assert(!Helper.ExprHasProperty(p, ExprFlag.EP_FromJoin));
                Debug.Assert((p.flags2 & Const.EP2_MallocedToken) == 0);
                Debug.Assert((p.flags2 & Const.EP2_Irreducible) == 0);
                if (p.pLeft != null || p.pRight != null || p.pColl != null || p.x.pList != null || p.x.pSelect != null)
                {
                    nSize = Const.EXPR_REDUCEDSIZE | ExprFlag.EP_Reduced;
                }
                else
                {
                    nSize = Const.EXPR_TOKENONLYSIZE | ExprFlag.EP_TokenOnly;
                }
            }
            return nSize;
        }

        /*
    ** This function returns the space in bytes required to store the copy
    ** of the Expr structure and a copy of the Expr.u.zToken string (if that
    ** string is defined.)
    */

        private static int dupedExprNodeSize(Expr p, int flags)
        {
            int nByte = dupedExprStructSize(p, flags) & 0xfff;
            if (!Helper.ExprHasProperty(p, ExprFlag.EP_IntValue) && p.u.zToken != null)
            {
                nByte += Utility.Sqlite3Strlen30(p.u.zToken) + 1;
            }
            return Utility.ROUND8(nByte);
        }

        /*
    ** Return the number of bytes required to create a duplicate of the
    ** expression passed as the first argument. The second argument is a
    ** mask containing EXPRDUP_XXX flags.
    **
    ** The value returned includes space to create a copy of the Expr struct
    ** itself and the buffer referred to by Expr.u.zToken, if any.
    **
    ** If the Const.EXPRDUP_REDUCE flag is set, then the return value includes
    ** space to duplicate all Expr nodes in the tree formed by Expr.pLeft
    ** and Expr.pRight variables (but not for any structures pointed to or
    ** descended from the Expr.x.pList or Expr.x.pSelect variables).
    */

        private static int dupedExprSize(Expr p, int flags)
        {
            int nByte = 0;
            if (p != null)
            {
                nByte = dupedExprNodeSize(p, flags);
                if ((flags & Const.EXPRDUP_REDUCE) != 0)
                {
                    nByte += dupedExprSize(p.pLeft, flags) + dupedExprSize(p.pRight, flags);
                }
            }
            return nByte;
        }

        /*
    ** This function is similar to ExprHelper.ExprDup(), except that if pzBuffer
    ** is not NULL then *pzBuffer is assumed to point to a buffer large enough
    ** to store the copy of expression p, the copies of p->u.zToken
    ** (if applicable), and the copies of the p->pLeft and p->pRight expressions,
    ** if any. Before returning, *pzBuffer is set to the first byte passed the
    ** portion of the buffer copied into by this function.
    */

        private static Expr exprDup(sqlite3 db, Expr p, int flags, ref Expr pzBuffer)
        {
            Expr pNew = null; /* Value to return */
            if (p != null)
            {
                bool isReduced = (flags & Const.EXPRDUP_REDUCE) != 0;
                //Expr zAlloc = new Expr();
                uint staticFlag = 0;

                Debug.Assert(pzBuffer == null || isReduced);

                /* Figure out where to write the new Expr structure. */
                //if ( pzBuffer !=null)
                //{
                //  zAlloc = pzBuffer;
                //  staticFlag = ExprFlag.EP_Static;
                //}
                //else
                //{
                //  zAlloc = new Expr();//sqlite3DbMallocRaw( db, dupedExprSize( p, flags ) );
                //}
                // (Expr*)zAlloc;

                //if ( pNew != null )
                {
                    /* Set nNewSize to the size allocated for the structure pointed to
          ** by pNew. This is either Const.EXPR_FULLSIZE, Const.EXPR_REDUCEDSIZE or
          ** Const.EXPR_TOKENONLYSIZE. nToken is set to the number of bytes consumed
          ** by the copy of the p->u.zToken string (if any).
          */
                    int nStructSize = dupedExprStructSize(p, flags);
                    int nNewSize = nStructSize & 0xfff;
                    int nToken;
                    if (!Helper.ExprHasProperty(p, ExprFlag.EP_IntValue) && !String.IsNullOrEmpty(p.u.zToken))
                    {
                        nToken = Utility.Sqlite3Strlen30(p.u.zToken);
                    }
                    else
                    {
                        nToken = 0;
                    }
                    if (isReduced)
                    {
                        Debug.Assert(!Helper.ExprHasProperty(p, ExprFlag.EP_Reduced));
                        pNew = p.Copy(Const.EXPR_TOKENONLYSIZE); //memcpy( zAlloc, p, nNewSize );
                    }
                    else
                    {
                        int nSize = exprStructSize(p);
                        //memcpy( zAlloc, p, nSize );
                        pNew = p.Copy();
                        //memset( &zAlloc[nSize], 0, Const.EXPR_FULLSIZE - nSize );
                    }

                    /* Set the EP_Reduced, ExprFlag.EP_TokenOnly, and ExprFlag.EP_Static flags appropriately. */
                    unchecked
                    {
                        pNew.flags &= (ushort) (~(ExprFlag.EP_TokenOnly | ExprFlag.EP_Reduced | ExprFlag.EP_Static));
                    }
                    pNew.flags |= (ushort) (nStructSize & (ExprFlag.EP_TokenOnly | ExprFlag.EP_Reduced));
                    pNew.flags |= (ushort) staticFlag;

                    /* Copy the p->u.zToken string, if any. */
                    if (nToken != 0)
                    {
                        string zToken; // = pNew.u.zToken = (char*)&zAlloc[nNewSize];
                        zToken = p.u.zToken.Substring(0, nToken); // memcpy( zToken, p.u.zToken, nToken );
                    }

                    if (0 == ((p.flags | pNew.flags) & ExprFlag.EP_TokenOnly))
                    {
                        /* Fill in the pNew.x.pSelect or pNew.x.pList member. */
                        if (Helper.ExprHasProperty(p, ExprFlag.EP_xIsSelect))
                        {
                            pNew.x.pSelect = SelectDup(db, p.x.pSelect, isReduced ? 1 : 0);
                        }
                        else
                        {
                            pNew.x.pList = ExprListDup(db, p.x.pList, isReduced ? 1 : 0);
                        }
                    }

                    /* Fill in pNew.pLeft and pNew.pRight. */
                    if (Helper.ExprHasAnyProperty(pNew, ExprFlag.EP_TokenOnly | ExprFlag.EP_Reduced))
                    {
                        //zAlloc += dupedExprNodeSize( p, flags );
                        if (Helper.ExprHasProperty(pNew, ExprFlag.EP_Reduced))
                        {
                            pNew.pLeft = exprDup(db, p.pLeft, Const.EXPRDUP_REDUCE, ref pzBuffer);
                            pNew.pRight = exprDup(db, p.pRight, Const.EXPRDUP_REDUCE, ref pzBuffer);
                        }
                        //if ( pzBuffer != null )
                        //{
                        //  pzBuffer = zAlloc;
                        //}
                    }
                    else
                    {
                        pNew.flags2 = 0;
                        if (!Helper.ExprHasAnyProperty(p, ExprFlag.EP_TokenOnly))
                        {
                            pNew.pLeft = ExprDup(db, p.pLeft, 0);
                            pNew.pRight = ExprDup(db, p.pRight, 0);
                        }
                    }
                }
            }
            return pNew;
        }

        /*
    ** The following group of routines make deep copies of expressions,
    ** expression lists, ID lists, and select statements.  The copies can
    ** be deleted (by being passed to their respective ...Delete() routines)
    ** without effecting the originals.
    **
    ** The expression list, ID, and source lists return by ExprHelper.ExprListDup(),
    ** ExprHelper.IdListDup(), and ExprHelper.SrcListDup() can not be further expanded
    ** by subsequent calls to sqlite*ListAppend() routines.
    **
    ** Any tables that the SrcList might point to are not duplicated.
    **
    ** The flags parameter contains a combination of the EXPRDUP_XXX flags.
    ** If the Const.EXPRDUP_REDUCE flag is set, then the structure returned is a
    ** truncated version of the usual Expr structure that will be stored as
    ** part of the in-memory representation of the database schema.
    */

        public static Expr ExprDup(sqlite3 db, Expr p, int flags)
        {
            Expr ExprDummy = null;
            return exprDup(db, p, flags, ref ExprDummy);
        }

        public static ExprList ExprListDup(sqlite3 db, ExprList p, int flags)
        {
            ExprList pNew;
            ExprList_item pItem;
            ExprList_item pOldItem;
            int i;
            if (p == null) return null;
            pNew = new ExprList(); //sqlite3DbMallocRaw(db, sizeof(*pNew) );
            //if ( pNew == null ) return null;
            pNew.iECursor = 0;
            pNew.nExpr = pNew.nAlloc = p.nExpr;
            pNew.a = new ExprList_item[p.nExpr]; //sqlite3DbMallocRaw(db,  p.nExpr*sizeof(p.a[0]) );
            //if( pItem==null ){
            //  MemPool.DbFree(db,ref pNew);
            //  return null;
            //}
            //pOldItem = p.a;
            for (i = 0; i < p.nExpr; i++)
            {
//pItem++, pOldItem++){
                pItem = pNew.a[i] = new ExprList_item();
                pOldItem = p.a[i];
                Expr pOldExpr = pOldItem.pExpr;
                pItem.pExpr = ExprDup(db, pOldExpr, flags);
                pItem.zName = pOldItem.zName; // sqlite3DbStrDup(db, pOldItem.zName);
                pItem.zSpan = pOldItem.zSpan; // sqlite3DbStrDup( db, pOldItem.zSpan );
                pItem.sortOrder = pOldItem.sortOrder;
                pItem.done = 0;
                pItem.iCol = pOldItem.iCol;
                pItem.iAlias = pOldItem.iAlias;
            }
            return pNew;
        }

        /*
    ** If cursors, triggers, views and subqueries are all omitted from
    ** the build, then none of the following routines, except for
    ** ExprHelper.SelectDup(), can be called. ExprHelper.SelectDup() is sometimes
    ** called with a NULL argument.
    */
#if !SQLITE_OMIT_VIEW || !SQLITE_OMIT_TRIGGER  || !SQLITE_OMIT_SUBQUERY
        public static SrcList SrcListDup(sqlite3 db, SrcList p, int flags)
        {
            SrcList pNew;
            int i;
            int nByte;
            if (p == null) return null;
            //nByte = sizeof(*p) + (p.nSrc>0 ? sizeof(p.a[0]) * (p.nSrc-1) : 0);
            pNew = new SrcList(); //sqlite3DbMallocRaw(db, nByte );
            if (p.nSrc > 0) pNew.a = new SrcList_item[p.nSrc];
            if (pNew == null) return null;
            pNew.nSrc = pNew.nAlloc = p.nSrc;
            for (i = 0; i < p.nSrc; i++)
            {
                pNew.a[i] = new SrcList_item();
                SrcList_item pNewItem = pNew.a[i];
                SrcList_item pOldItem = p.a[i];
                Table pTab;
                pNewItem.zDatabase = pOldItem.zDatabase; // sqlite3DbStrDup(db, pOldItem.zDatabase);
                pNewItem.zName = pOldItem.zName; // sqlite3DbStrDup(db, pOldItem.zName);
                pNewItem.zAlias = pOldItem.zAlias; // sqlite3DbStrDup(db, pOldItem.zAlias);
                pNewItem.jointype = pOldItem.jointype;
                pNewItem.iCursor = pOldItem.iCursor;
                pNewItem.isPopulated = pOldItem.isPopulated;
                pNewItem.zIndex = pOldItem.zIndex; // sqlite3DbStrDup( db, pOldItem.zIndex );
                pNewItem.notIndexed = pOldItem.notIndexed;
                pNewItem.pIndex = pOldItem.pIndex;
                pTab = pNewItem.pTab = pOldItem.pTab;
                if (pTab != null)
                {
                    pTab.nRef++;
                }
                pNewItem.pSelect = SelectDup(db, pOldItem.pSelect, flags);
                pNewItem.pOn = ExprDup(db, pOldItem.pOn, flags);
                pNewItem.pUsing = IdListDup(db, pOldItem.pUsing);
                pNewItem.colUsed = pOldItem.colUsed;
            }
            return pNew;
        }

        public static IdList IdListDup(sqlite3 db, IdList p)
        {
            IdList pNew;
            int i;
            if (p == null) return null;
            pNew = new IdList(); //sqlite3DbMallocRaw(db, sizeof(*pNew) );
            if (pNew == null) return null;
            pNew.nId = pNew.nAlloc = p.nId;
            pNew.a = new IdList_item[p.nId]; //sqlite3DbMallocRaw(db, p.nId*sizeof(p.a[0]) );
            if (pNew.a == null)
            {
                MemPool.DbFree(db, ref pNew);
                return null;
            }
            for (i = 0; i < p.nId; i++)
            {
                pNew.a[i] = new IdList_item();
                IdList_item pNewItem = pNew.a[i];
                IdList_item pOldItem = p.a[i];
                pNewItem.zName = pOldItem.zName; // sqlite3DbStrDup(db, pOldItem.zName);
                pNewItem.idx = pOldItem.idx;
            }
            return pNew;
        }

        public static Select SelectDup(sqlite3 db, Select p, int flags)
        {
            Select pNew;
            if (p == null) return null;
            pNew = new Select(); //sqlite3DbMallocRaw(db, sizeof(*p) );
            //if ( pNew == null ) return null;
            pNew.pEList = ExprListDup(db, p.pEList, flags);
            pNew.pSrc = SrcListDup(db, p.pSrc, flags);
            pNew.pWhere = ExprDup(db, p.pWhere, flags);
            pNew.pGroupBy = ExprListDup(db, p.pGroupBy, flags);
            pNew.pHaving = ExprDup(db, p.pHaving, flags);
            pNew.pOrderBy = ExprListDup(db, p.pOrderBy, flags);
            pNew.op = p.op;
            pNew.pPrior = SelectDup(db, p.pPrior, flags);
            pNew.pLimit = ExprDup(db, p.pLimit, flags);
            pNew.pOffset = ExprDup(db, p.pOffset, flags);
            pNew.iLimit = 0;
            pNew.iOffset = 0;
            pNew.selFlags = (ushort) (p.selFlags & ~SelectFlag.SF_UsesEphemeral);
            pNew.pRightmost = null;
            pNew.addrOpenEphm[0] = -1;
            pNew.addrOpenEphm[1] = -1;
            pNew.addrOpenEphm[2] = -1;
            return pNew;
        }
#else
Select ExprHelper.SelectDup(sqlite3 db, Select p, int flags){
Debug.Assert( p==null );
return null;
}
#endif


        /*
** Add a new element to the end of an expression list.  If pList is
** initially NULL, then create a new expression list.
**
** If a memory allocation error occurs, the entire list is freed and
** NULL is returned.  If non-NULL is returned, then it is guaranteed
** that the new entry was successfully appended.
*/
        // OVERLOADS, so I don't need to rewrite parse.c
        public static ExprList ExprListAppend(Parse pParse, int null_2, Expr pExpr)
        {
            return ExprListAppend(pParse, null, pExpr);
        }

        public static ExprList ExprListAppend(
            Parse pParse, /* Parsing context */
            ExprList pList, /* List to which to append. Might be NULL */
            Expr pExpr /* Expression to be appended. Might be NULL */
            )
        {
            sqlite3 db = pParse.db;
            if (pList == null)
            {
                pList = new ExprList(); //Malloc.DbMallocZero(db, ExprList).Length;
                //if ( pList == null )
                //{
                //  goto no_mem;
                //}
                Debug.Assert(pList.nAlloc == 0);
            }
            if (pList.nAlloc <= pList.nExpr)
            {
                ExprList_item a;
                int n = pList.nAlloc*2 + 4;
                //a = Malloc.DbRealloc(db, pList.a, n*sizeof(pList.a[0]));
                //if( a==0 ){
                //  goto no_mem;
                //}
                Array.Resize(ref pList.a, n); // = a;
                pList.nAlloc = pList.a.Length; // sqlite3DbMallocSize(db, a)/sizeof(a[0]);
            }
            Debug.Assert(pList.a != null);
            if (true)
            {
                pList.a[pList.nExpr] = new ExprList_item();
                //ExprList_item pItem = pList.a[pList.nExpr++];
                //pItem = new ExprList_item();//memset(pItem, 0, sizeof(*pItem));
                //pItem.pExpr = pExpr;
                pList.a[pList.nExpr++].pExpr = pExpr;
            }
            return pList;

            //no_mem:
            //  /* Avoid leaking memory if malloc has failed. */
            //  ExprHeler.ExprDelete( db, ref pExpr );
            //  ExprHelper.ExprListDelete( db, ref pList );
            //  return null;
        }

        /*
    ** Set the ExprList.a[].zName element of the most recently added item
    ** on the expression list.
    **
    ** pList might be NULL following an OOM error.  But pName should never be
    ** NULL.  If a memory allocation fails, the pParse.db.mallocFailed flag
    ** is set.
    */

        public static void ExprListSetName(
            Parse pParse, /* Parsing context */
            ExprList pList, /* List to which to add the span. */
            Token pName, /* Name to be added */
            int dequote /* True to cause the name to be dequoted */
            )
        {
            Debug.Assert(pList != null /* || pParse.db.mallocFailed != 0 */);
            if (pList != null)
            {
                ExprList_item pItem;
                Debug.Assert(pList.nExpr > 0);
                pItem = pList.a[pList.nExpr - 1];
                Debug.Assert(pItem.zName == null);
                pItem.zName = pName.z.Substring(0, pName.n); //sqlite3DbStrNDup(pParse.db, pName.z, pName.n);
                if (dequote != 0 && !String.IsNullOrEmpty(pItem.zName)) Utility.Sqlite3Dequote(ref pItem.zName);
            }
        }

        /*
    ** Set the ExprList.a[].zSpan element of the most recently added item
    ** on the expression list.
    **
    ** pList might be NULL following an OOM error.  But pSpan should never be
    ** NULL.  If a memory allocation fails, the pParse.db.mallocFailed flag
    ** is set.
    */

        public static void ExprListSetSpan(
            Parse pParse, /* Parsing context */
            ExprList pList, /* List to which to add the span. */
            ExprSpan pSpan /* The span to be added */
            )
        {
            sqlite3 db = pParse.db;
            Debug.Assert(pList != null /*|| db.mallocFailed != 0 */);
            if (pList != null)
            {
                ExprList_item pItem = pList.a[pList.nExpr - 1];
                Debug.Assert(pList.nExpr > 0);
                Debug.Assert( /* db.mallocFailed != 0 || */ pItem.pExpr == pSpan.pExpr);
                MemPool.DbFree(db, ref pItem.zSpan);
                pItem.zSpan = pSpan.zStart.Substring(0,
                                                     pSpan.zStart.Length <= pSpan.zEnd.Length
                                                         ? pSpan.zStart.Length
                                                         : pSpan.zStart.Length - pSpan.zEnd.Length);
                    // sqlite3DbStrNDup( db, pSpan.zStart,
                //(int)( pSpan.zEnd- pSpan.zStart) );
            }
        }

        /*
    ** If the expression list pEList contains more than iLimit elements,
    ** leave an error message in pParse.
    */

        public static void ExprListCheckLength(
            Parse pParse,
            ExprList pEList,
            string zObject
            )
        {
            int mx = pParse.db.aLimit[LimitCategory.SQLITE_LIMIT_COLUMN];
            UnitTest.TestCase(pEList != null && pEList.nExpr == mx);
            UnitTest.TestCase(pEList != null && pEList.nExpr == mx + 1);
            if (pEList != null && pEList.nExpr > mx)
            {
                Utility.Sqlite3ErrorMsg(pParse, "too many columns in %s", zObject);
            }
        }


        /*
    ** Delete an entire expression list.
    */

        public static void ExprListDelete(sqlite3 db, ref ExprList pList)
        {
            int i;
            ExprList_item pItem;
            if (pList == null) return;
            Debug.Assert(pList.a != null || (pList.nExpr == 0 && pList.nAlloc == 0));
            Debug.Assert(pList.nExpr <= pList.nAlloc);
            for (i = 0; i < pList.nExpr; i++)
            {
                if ((pItem = pList.a[i]) != null)
                {
                    ExprDelete(db, ref pItem.pExpr);
                    MemPool.DbFree(db, ref pItem.zName);
                    MemPool.DbFree(db, ref pItem.zSpan);
                }
            }
            MemPool.DbFree(db, ref pList.a);
            MemPool.DbFree(db, ref pList);
        }

        /*
    ** These routines are Walker callbacks.  Walker.u.pi is a pointer
    ** to an integer.  These routines are checking an expression to see
    ** if it is a constant.  Set *Walker.u.pi to 0 if the expression is
    ** not constant.
    **
    ** These callback routines are used to implement the following:
    **
    **     ExprHelper.ExprIsConstant()
    **     ExprHelper.ExprIsConstantNotJoin()
    **     ExprHelper.ExprIsConstantOrFunction()
    **
    */

        private static int exprNodeIsConstant(Walker pWalker, ref Expr pExpr)
        {
            /* If pWalker.u.i is 3 then any term of the expression that comes from
      ** the ON or USING clauses of a join disqualifies the expression
      ** from being considered constant. */
            if (pWalker.u.i == 3 && Helper.ExprHasAnyProperty(pExpr, ExprFlag.EP_FromJoin))
            {
                pWalker.u.i = 0;
                return Wrc.WRC_Abort;
            }

            switch (pExpr.op)
            {
                    /* Consider functions to be constant if all their arguments are constant
        ** and pWalker.u.i==2 */
                case TokenKeyword.TK_FUNCTION:
                    if ((pWalker.u.i) == 2) return 0;
                    goto case TokenKeyword.TK_ID;
                    /* Fall through */
                case TokenKeyword.TK_ID:
                case TokenKeyword.TK_COLUMN:
                case TokenKeyword.TK_AGG_FUNCTION:
                case TokenKeyword.TK_AGG_COLUMN:
                    UnitTest.TestCase(pExpr.op == TokenKeyword.TK_ID);
                    UnitTest.TestCase(pExpr.op == TokenKeyword.TK_COLUMN);
                    UnitTest.TestCase(pExpr.op == TokenKeyword.TK_AGG_FUNCTION);
                    UnitTest.TestCase(pExpr.op == TokenKeyword.TK_AGG_COLUMN);
                    pWalker.u.i = 0;
                    return Wrc.WRC_Abort;
                default:
                    UnitTest.TestCase(pExpr.op == TokenKeyword.TK_SELECT); /* selectNodeIsConstant will disallow */
                    UnitTest.TestCase(pExpr.op == TokenKeyword.TK_EXISTS); /* selectNodeIsConstant will disallow */
                    return Wrc.WRC_Continue;
            }
        }

        private static int selectNodeIsConstant(Walker pWalker, Select NotUsed)
        {
            Helper.UNUSED_PARAMETER(NotUsed);
            pWalker.u.i = 0;
            return Wrc.WRC_Abort;
        }

        private static int exprIsConst(Expr p, int initFlag)
        {
            var w = new Walker();
            w.u.i = initFlag;
            w.xExprCallback = exprNodeIsConstant;
            w.xSelectCallback = selectNodeIsConstant;
            sqlite3WalkExpr(w, ref p);
            return w.u.i;
        }

        /*
    ** Walk an expression tree.  Return 1 if the expression is constant
    ** and 0 if it involves variables or function calls.
    **
    ** For the purposes of this function, a double-quoted string (ex: "abc")
    ** is considered a variable but a single-quoted string (ex: 'abc') is
    ** a constant.
    */

        public static int ExprIsConstant(Expr p)
        {
            return exprIsConst(p, 1);
        }

        /*
    ** Walk an expression tree.  Return 1 if the expression is constant
    ** that does no originate from the ON or USING clauses of a join.
    ** Return 0 if it involves variables or function calls or terms from
    ** an ON or USING clause.
    */

        public static int ExprIsConstantNotJoin(Expr p)
        {
            return exprIsConst(p, 3);
        }

        /*
    ** Walk an expression tree.  Return 1 if the expression is constant
    ** or a function call with constant arguments.  Return and 0 if there
    ** are any variables.
    **
    ** For the purposes of this function, a double-quoted string (ex: "abc")
    ** is considered a variable but a single-quoted string (ex: 'abc') is
    ** a constant.
    */

        public static int ExprIsConstantOrFunction(Expr p)
        {
            return exprIsConst(p, 2);
        }

        /*
    ** If the expression p codes a constant integer that is small enough
    ** to fit in a 32-bit integer, return 1 and put the value of the integer
    ** in pValue.  If the expression is not an integer or if it is too big
    ** to fit in a signed 32-bit integer, return 0 and leave pValue unchanged.
    */

        public static int ExprIsInteger(Expr p, ref int pValue)
        {
            int rc = 0;
            if ((p.flags & ExprFlag.EP_IntValue) != 0)
            {
                pValue = p.u.iValue;
                return 1;
            }
            switch (p.op)
            {
                case TokenKeyword.TK_INTEGER:
                    {
                        rc = Utility.Sqlite3GetInt32(p.u.zToken, ref pValue) ? 1 : 0;
                        Debug.Assert(rc == 0);
                        break;
                    }
                case TokenKeyword.TK_UPLUS:
                    {
                        rc = ExprIsInteger(p.pLeft, ref pValue);
                        break;
                    }
                case TokenKeyword.TK_UMINUS:
                    {
                        int v = 0;
                        if (ExprIsInteger(p.pLeft, ref v) != 0)
                        {
                            pValue = -v;
                            rc = 1;
                        }
                        break;
                    }
                default:
                    break;
            }
            if (rc != 0)
            {
                Debug.Assert(Helper.ExprHasAnyProperty(p, ExprFlag.EP_TokenOnly | ExprFlag.EP_Reduced)
                             || (p.flags2 & Const.EP2_MallocedToken) == 0);
                p.op = TokenKeyword.TK_INTEGER;
                p.flags |= ExprFlag.EP_IntValue;
                p.u.iValue = pValue;
            }
            return rc;
        }

        /*
    ** Return FALSE if there is no chance that the expression can be NULL.
    **
    ** If the expression might be NULL or if the expression is too complex
    ** to tell return TRUE.  
    **
    ** This routine is used as an optimization, to skip OPCode.OP_IsNull opcodes
    ** when we know that a value cannot be NULL.  Hence, a false positive
    ** (returning TRUE when in fact the expression can never be NULL) might
    ** be a small performance hit but is otherwise harmless.  On the other
    ** hand, a false negative (returning FALSE when the result could be NULL)
    ** will Utility.Likely result in an incorrect answer.  So when in doubt, return
    ** TRUE.
    */

        public static int ExprCanBeNull(Expr p)
        {
            byte op;
            while (p.op == TokenKeyword.TK_UPLUS || p.op == TokenKeyword.TK_UMINUS)
            {
                p = p.pLeft;
            }
            op = p.op;
            if (op == TokenKeyword.TK_REGISTER) op = p.op2;
            switch (op)
            {
                case TokenKeyword.TK_INTEGER:
                case TokenKeyword.TK_STRING:
                case TokenKeyword.TK_FLOAT:
                case TokenKeyword.TK_BLOB:
                    return 0;
                default:
                    return 1;
            }
        }

        /*
    ** Generate an OPCode.OP_IsNull instruction that tests register iReg and jumps
    ** to location iDest if the value in iReg is NULL.  The value in iReg 
    ** was computed by pExpr.  If we can look at pExpr at compile-time and
    ** determine that it can never generate a NULL, then the OPCode.OP_IsNull operation
    ** can be omitted.
    */

        public static void ExprCodeIsNullJump(
            Vdbe v, /* The VDBE under construction */
            Expr pExpr, /* Only generate OPCode.OP_IsNull if this expr can be NULL */
            int iReg, /* Test the value in this register for NULL */
            int iDest /* Jump here if the value is null */
            )
        {
            if (ExprCanBeNull(pExpr) != 0)
            {
                VdbeAux.VdbeAddOp2(v, OPCode.OP_IsNull, iReg, iDest);
            }
        }

        /*
    ** Return TRUE if the given expression is a constant which would be
    ** unchanged by OPCode.OP_Affinity with the affinity given in the second
    ** argument.
    **
    ** This routine is used to determine if the OPCode.OP_Affinity operation
    ** can be omitted.  When in doubt return FALSE.  A false negative
    ** is harmless.  A false positive, however, can result in the wrong
    ** answer.
    */

        public static int ExprNeedsNoAffinityChange(Expr p, char aff)
        {
            byte op;
            if (aff == ColumnAffinityType.SQLITE_AFF_NONE) return 1;
            while (p.op == TokenKeyword.TK_UPLUS || p.op == TokenKeyword.TK_UMINUS)
            {
                p = p.pLeft;
            }
            op = p.op;
            if (op == TokenKeyword.TK_REGISTER) op = p.op2;
            switch (op)
            {
                case TokenKeyword.TK_INTEGER:
                    {
                        return (aff == ColumnAffinityType.SQLITE_AFF_INTEGER || aff == ColumnAffinityType.SQLITE_AFF_NUMERIC) ? 1 : 0;
                    }
                case TokenKeyword.TK_FLOAT:
                    {
                        return (aff == ColumnAffinityType.SQLITE_AFF_REAL || aff == ColumnAffinityType.SQLITE_AFF_NUMERIC) ? 1 : 0;
                    }
                case TokenKeyword.TK_STRING:
                    {
                        return (aff == ColumnAffinityType.SQLITE_AFF_TEXT) ? 1 : 0;
                    }
                case TokenKeyword.TK_BLOB:
                    {
                        return 1;
                    }
                case TokenKeyword.TK_COLUMN:
                    {
                        Debug.Assert(p.iTable >= 0); /* p cannot be part of a CHECK constraint */
                        return (p.iColumn < 0
                                && (aff == ColumnAffinityType.SQLITE_AFF_INTEGER || aff == ColumnAffinityType.SQLITE_AFF_NUMERIC))
                                   ? 1
                                   : 0;
                    }
                default:
                    {
                        return 0;
                    }
            }
        }

        /*
    ** Return TRUE if the given string is a row-id column name.
    */

        public static bool IsRowid(string z)
        {
            if (Utility.Sqlite3StrICmp(z, "_ROWID_") == 0) return true;
            if (Utility.Sqlite3StrICmp(z, "ROWID") == 0) return true;
            if (Utility.Sqlite3StrICmp(z, "OID") == 0) return true;
            return false;
        }


        /*
    ** Return true if we are able to the IN operator optimization on a
    ** query of the form
    **
    **       x IN (SELECT ...)
    **
    ** Where the SELECT... clause is as specified by the parameter to this
    ** routine.
    **
    ** The Select object passed in has already been preprocessed and no
    ** errors have been found.
    */
#if !SQLITE_OMIT_SUBQUERY
        private static int isCandidateForInOpt(Select p)
        {
            SrcList pSrc;
            ExprList pEList;
            Table pTab;
            if (p == null) return 0; /* right-hand side of IN is SELECT */
            if (p.pPrior != null) return 0; /* Not a compound SELECT */
            if ((p.selFlags & (SelectFlag.SF_Distinct | SelectFlag.SF_Aggregate)) != 0)
            {
                UnitTest.TestCase((p.selFlags & (SelectFlag.SF_Distinct | SelectFlag.SF_Aggregate)) == SelectFlag.SF_Distinct);
                UnitTest.TestCase((p.selFlags & (SelectFlag.SF_Distinct | SelectFlag.SF_Aggregate)) == SelectFlag.SF_Aggregate);
                return 0; /* No DISTINCT keyword and no aggregate functions */
            }
            Debug.Assert(p.pGroupBy == null); /* Has no GROUP BY clause */
            if (p.pLimit != null) return 0; /* Has no LIMIT clause */
            Debug.Assert(p.pOffset == null); /* No LIMIT means no OFFSET */

            if (p.pWhere != null) return 0; /* Has no WHERE clause */
            pSrc = p.pSrc;
            Debug.Assert(pSrc != null);
            if (pSrc.nSrc != 1) return 0; /* Single term in FROM clause */
            if (pSrc.a[0].pSelect != null) return 0; /* FROM is not a subquery or view */
            pTab = pSrc.a[0].pTab;
            if (UnitTest.NEVER(pTab == null)) return 0;
            Debug.Assert(pTab.pSelect == null); /* FROM clause is not a view */
            if (Utility.IsVirtual(pTab)) return 0; /* FROM clause not a virtual table */
            pEList = p.pEList;
            if (pEList.nExpr != 1) return 0; /* One column in the result set */
            if (pEList.a[0].pExpr.op != TokenKeyword.TK_COLUMN) return 0; /* Result is a column */
            return 1;
        }
#endif
        //* SQLITE_OMIT_SUBQUERY */

        /*
** This function is used by the implementation of the IN (...) operator.
** It's job is to find or create a b-tree structure that may be used
** either to test for membership of the (...) set or to iterate through
** its members, skipping duplicates.
**
** The index of the cursor opened on the b-tree (database table, database index
** or ephermal table) is stored in pX->iTable before this function returns.
** The returned value of this function indicates the b-tree type, as follows:
**
**   Const.IN_INDEX_ROWID - The cursor was opened on a database table.
**   Const.IN_INDEX_INDEX - The cursor was opened on a database index.
**   Const.IN_INDEX_EPH -   The cursor was opened on a specially created and
**                    populated epheremal table.
**
** An existing b-tree may only be used if the SELECT is of the simple
** form:
**
**     SELECT <column> FROM <table>
**
** If the prNotFound parameter is 0, then the b-tree will be used to iterate
** through the set members, skipping any duplicates. In this case an
** epheremal table must be used unless the selected <column> is guaranteed
** to be unique - either because it is an INTEGER PRIMARY KEY or it
** has a UNIQUE constraint or UNIQUE index.
**
** If the prNotFound parameter is not 0, then the b-tree will be used
** for fast set membership tests. In this case an epheremal table must
** be used unless <column> is an INTEGER PRIMARY KEY or an index can
** be found with <column> as its left-most column.
**
** When the b-tree is being used for membership tests, the calling function
** needs to know whether or not the structure contains an SQL NULL 
** value in order to correctly evaluate expressions like "X IN (Y, Z)".
** If there is any chance that the (...) might contain a NULL value at
** runtime, then a register is allocated and the register number written
** to *prNotFound. If there is no chance that the (...) contains a
** NULL value, then *prNotFound is left unchanged.
**
** If a register is allocated and its location stored in *prNotFound, then
** its initial value is NULL.  If the (...) does not remain constant
** for the duration of the query (i.e. the SELECT within the (...)
** is a correlated subquery) then the value of the allocated register is
** reset to NULL each time the subquery is rerun. This allows the
** caller to use vdbe code equivalent to the following:
**
**   if( register==NULL ){
**     has_null = <test if data structure contains null>
**     register = 1
**   }
**
** in order to avoid running the <test if data structure contains null>
** test more often than is necessary.
*/
#if !SQLITE_OMIT_SUBQUERY
        public static int FindInIndex(Parse pParse, Expr pX, ref int prNotFound)
        {
            Select p; /* SELECT to the right of IN operator */
            int eType = 0; /* Type of RHS table. IN_INDEX_* */
            int iTab = pParse.nTab++; /* Cursor of the RHS table */
            bool mustBeUnique = (prNotFound != 0); /* True if RHS must be unique */

            Debug.Assert(pX.op == TokenKeyword.TK_IN);

            /* Check to see if an existing table or index can be used to
      ** satisfy the query.  This is preferable to generating a new
      ** ephemeral table.
      */
            p = (Helper.ExprHasProperty(pX, ExprFlag.EP_xIsSelect) ? pX.x.pSelect : null);
            if (UnitTest.ALWAYS(pParse.nErr == 0) && isCandidateForInOpt(p) != 0)
            {
                sqlite3 db = pParse.db; /* Database connection */
                Expr pExpr = p.pEList.a[0].pExpr; /* Expression <column> */
                int iCol = pExpr.iColumn; /* Index of column <column> */
                Vdbe v = SelectHelper.GetVdbe(pParse); /* Virtual machine being coded */
                Table pTab = p.pSrc.a[0].pTab; /* Table <table>. */
                int iDb; /* Database idx for pTab */

                /* Code an OPCode.OP_VerifyCookie and OPCode.OP_TableLock for <table>. */
                iDb = sqlite3SchemaToIndex(db, pTab.pSchema);
                Build.CodeVerifySchema(pParse, iDb);
                sqlite3TableLock(pParse, iDb, pTab.tnum, 0, pTab.zName);

                /* This function is only called from two places. In both cases the vdbe
        ** has already been allocated. So assume SelectHelper.GetVdbe() is always
        ** successful here.
        */
                Debug.Assert(v != null);
                if (iCol < 0)
                {
                    int iMem = ++pParse.nMem;
                    int iAddr;

                    iAddr = VdbeAux.VdbeAddOp1(v, OPCode.OP_If, iMem);
                    VdbeAux.VdbeAddOp2(v, OPCode.OP_Integer, 1, iMem);

                    sqlite3OpenTable(pParse, iTab, iDb, pTab, OPCode.OP_OpenRead);
                    eType = Const.IN_INDEX_ROWID;

                    VdbeAux.VdbeJumpHere(v, iAddr);
                }
                else
                {
                    Index pIdx; /* Iterator variable */
                    /* The collation sequence used by the comparison. If an index is to
          ** be used in place of a temp.table, it must be ordered according
          ** to this collation sequence. */
                    CollSeq pReq = BinaryCompareCollSeq(pParse, pX.pLeft, pExpr);

                    /* Check that the affinity that will be used to perform the
          ** comparison is the same as the affinity of the column. If
          ** it is not, it is not possible to use any index.
          */
                    char aff = comparisonAffinity(pX);
                    bool affinity_ok = (pTab.aCol[iCol].affinity == aff || aff == ColumnAffinityType.SQLITE_AFF_NONE);

                    for (pIdx = pTab.pIndex; pIdx != null && eType == 0 && affinity_ok; pIdx = pIdx.pNext)
                    {
                        if ((pIdx.aiColumn[0] == iCol)
                            && (Callback.FindCollSeq(db, Helper.ENC(db), pIdx.azColl[0], 0) == pReq)
                            && (mustBeUnique == false || (pIdx.nColumn == 1 && pIdx.onError != OnConstraintError.OE_None))
                            )
                        {
                            int iMem = ++pParse.nMem;
                            int iAddr;
                            KeyInfo pKey;

                            pKey = Build.IndexKeyinfo(pParse, pIdx);

                            iAddr = VdbeAux.VdbeAddOp1(v, OPCode.OP_If, iMem);
                            VdbeAux.VdbeAddOp2(v, OPCode.OP_Integer, 1, iMem);

                            VdbeAux.VdbeAddOp4(v, OPCode.OP_OpenRead, iTab, pIdx.tnum, iDb,
                                              pKey, P4Type.P4_KEYINFO_HANDOFF);
#if SQLITE_DEBUG
                            VdbeComment(v, "%s", pIdx.zName);
#endif
                            eType = Const.IN_INDEX_INDEX;

                            VdbeAux.VdbeJumpHere(v, iAddr);
                            if ( //prNotFound != null &&         -- always exists under C#
                                pTab.aCol[iCol].notNull == 0)
                            {
                                prNotFound = ++pParse.nMem;
                            }
                        }
                    }
                }
            }

            if (eType == 0)
            {
                /* Could not found an existing table or index to use as the RHS b-tree.
        ** We will have to generate an ephemeral table to do the job.
        */
                int rMayHaveNull = 0;
                eType = Const.IN_INDEX_EPH;
                if (prNotFound != -1) // Klude to show prNotFound not available
                {
                    prNotFound = rMayHaveNull = ++pParse.nMem;
                }
                else if (pX.pLeft.iColumn < 0 && !Helper.ExprHasAnyProperty(pX, ExprFlag.EP_xIsSelect))
                {
                    eType = Const.IN_INDEX_ROWID;
                }
                CodeSubselect(pParse, pX, rMayHaveNull, eType == Const.IN_INDEX_ROWID);
            }
            else
            {
                pX.iTable = iTab;
            }
            return eType;
        }
#endif

        /*
** Generate code for scalar subqueries used as an expression
** and IN operators.  Examples:
**
**     (SELECT a FROM b)          -- subquery
**     EXISTS (SELECT a FROM b)   -- EXISTS subquery
**     x IN (4,5,11)              -- IN operator with list on right-hand side
**     x IN (SELECT a FROM b)     -- IN operator with subquery on the right
**
** The pExpr parameter describes the expression that contains the IN
** operator or subquery.
**
** If parameter isRowid is non-zero, then expression pExpr is guaranteed
** to be of the form "<rowid> IN (?, ?, ?)", where <rowid> is a reference
** to some integer key column of a table B-Tree. In this case, use an
** intkey B-Tree to store the set of IN(...) values instead of the usual
** (slower) variable length keys B-Tree.
**
** If rMayHaveNull is non-zero, that means that the operation is an IN
** (not a SELECT or EXISTS) and that the RHS might contains NULLs.
** Furthermore, the IN is in a WHERE clause and that we really want
** to iterate over the RHS of the IN operator in order to quickly locate
** all corresponding LHS elements.  All this routine does is initialize
** the register given by rMayHaveNull to NULL.  Calling routines will take
** care of changing this register value to non-NULL if the RHS is NULL-free.
**
** If rMayHaveNull is zero, that means that the subquery is being used
** for membership testing only.  There is no need to initialize any
** registers to indicate the presense or absence of NULLs on the RHS.
**
** For a SELECT or EXISTS operator, return the register that holds the
** result.  For IN operators or if an error occurs, the return value is 0.
*/
#if !SQLITE_OMIT_SUBQUERY
        public static int CodeSubselect(
            Parse pParse, /* Parsing context */
            Expr pExpr, /* The IN, SELECT, or EXISTS operator */
            int rMayHaveNull, /* Register that records whether NULLs exist in RHS */
            bool isRowid /* If true, LHS of IN operator is a rowid */
            )
        {
            int testAddr = 0; /* One-time test address */
            int rReg = 0; /* Register storing resulting */
            Vdbe v = SelectHelper.GetVdbe(pParse);
            if (UnitTest.NEVER(v == null)) return 0;
            ExprCachePush(pParse);

            /* This code must be run in its entirety every time it is encountered
      ** if any of the following is true:
      **
      **    *  The right-hand side is a correlated subquery
      **    *  The right-hand side is an expression list containing variables
      **    *  We are inside a trigger
      **
      ** If all of the above are false, then we can run this code just once
      ** save the results, and reuse the same result on subsequent invocations.
      */
            if (!Helper.ExprHasAnyProperty(pExpr, ExprFlag.EP_VarSelect) && null == pParse.pTriggerTab)
            {
                int mem = ++pParse.nMem;
                VdbeAux.VdbeAddOp1(v, OPCode.OP_If, mem);
                testAddr = VdbeAux.VdbeAddOp2(v, OPCode.OP_Integer, 1, mem);
                Debug.Assert(testAddr > 0 /* || pParse.db.mallocFailed != 0 */);
            }

            switch (pExpr.op)
            {
                case TokenKeyword.TK_IN:
                    {
                        char affinity;
                        KeyInfo keyInfo;
                        int addr; /* Address of OPCode.OP_OpenEphemeral instruction */
                        Expr pLeft = pExpr.pLeft;

                        if (rMayHaveNull != 0)
                        {
                            VdbeAux.VdbeAddOp2(v, OPCode.OP_Null, 0, rMayHaveNull);
                        }

                        affinity = ExprAffinity(pLeft);

                        /* Whether this is an 'x IN(SELECT...)' or an 'x IN(<exprlist>)'
            ** expression it is handled the same way. An ephemeral table is
            ** filled with single-field index keys representing the results
            ** from the SELECT or the <exprlist>.
            **
            ** If the 'x' expression is a column value, or the SELECT...
            ** statement returns a column value, then the affinity of that
            ** column is used to build the index keys. If both 'x' and the
            ** SELECT... statement are columns, then numeric affinity is used
            ** if either column has NUMERIC or INTEGER affinity. If neither
            ** 'x' nor the SELECT... statement are columns, then numeric affinity
            ** is used.
            */
                        pExpr.iTable = pParse.nTab++;
                        addr = VdbeAux.VdbeAddOp2(v, OPCode.OP_OpenEphemeral, pExpr.iTable, !isRowid);
                        keyInfo = new KeyInfo(); // memset( &keyInfo, 0, sizeof(keyInfo ));
                        keyInfo.nField = 1;

                        if (Helper.ExprHasProperty(pExpr, ExprFlag.EP_xIsSelect))
                        {
                            /* Case 1:     expr IN (SELECT ...)
              **
              ** Generate code to write the results of the select into the temporary
              ** table allocated and opened above.
              */
                            var dest = new SelectDest();
                            ExprList pEList;

                            Debug.Assert(!isRowid);
                            sqlite3SelectDestInit(dest, SelectResultType.SRT_Set, pExpr.iTable);
                            dest.affinity = affinity;
                            Debug.Assert((pExpr.iTable & 0x0000FFFF) == pExpr.iTable);
                            if (sqlite3Select(pParse, pExpr.x.pSelect, ref dest) != 0)
                            {
                                return 0;
                            }
                            pEList = pExpr.x.pSelect.pEList;
                            if (UnitTest.ALWAYS(pEList != null) && pEList.nExpr > 0)
                            {
                                keyInfo.aColl[0] = BinaryCompareCollSeq(pParse, pExpr.pLeft,
                                                                               pEList.a[0].pExpr);
                            }
                        }
                        else if (pExpr.x.pList != null)
                        {
                            /* Case 2:     expr IN (exprlist)
              **
              ** For each expression, build an index key from the evaluation and
              ** store it in the temporary table. If <expr> is a column, then use
              ** that columns affinity when building index keys. If <expr> is not
              ** a column, use numeric affinity.
              */
                            int i;
                            ExprList pList = pExpr.x.pList;
                            ExprList_item pItem;
                            int r1, r2, r3;

                            if (affinity == '\0')
                            {
                                affinity = ColumnAffinityType.SQLITE_AFF_NONE;
                            }
                            keyInfo.aColl[0] = ExprCollSeq(pParse, pExpr.pLeft);

                            /* Loop through each expression in <exprlist>. */
                            r1 = GetTempReg(pParse);
                            r2 = GetTempReg(pParse);
                            VdbeAux.VdbeAddOp2(v, OPCode.OP_Null, 0, r2);
                            for (i = 0; i < pList.nExpr; i++)
                            {
//, pItem++){
                                pItem = pList.a[i];
                                Expr pE2 = pItem.pExpr;
                                int iValToIns = 0;

                                /* If the expression is not constant then we will need to
                ** disable the test that was generated above that makes sure
                ** this code only executes once.  Because for a non-constant
                ** expression we need to rerun this code each time.
                */
                                if (testAddr != 0 && ExprIsConstant(pE2) == 0)
                                {
                                    sqlite3VdbeChangeToNoop(v, testAddr - 1, 2);
                                    testAddr = 0;
                                }

                                /* Evaluate the expression and insert it into the temp table */
                                if (isRowid && ExprIsInteger(pE2, ref iValToIns) != 0)
                                {
                                    VdbeAux.VdbeAddOp3(v, OPCode.OP_InsertInt, pExpr.iTable, r2, iValToIns);
                                }
                                else
                                {
                                    r3 = ExprCodeTarget(pParse, pE2, r1);
                                    if (isRowid)
                                    {
                                        VdbeAux.VdbeAddOp2(v, OPCode.OP_MustBeInt, r3,
                                                          VdbeAux.VdbeCurrentAddr(v) + 2);
                                        VdbeAux.VdbeAddOp3(v, OPCode.OP_Insert, pExpr.iTable, r2, r3);
                                    }
                                    else
                                    {
                                        VdbeAux.VdbeAddOp4(v, OPCode.OP_MakeRecord, r3, 1, r2, affinity, 1);
                                        ExprCacheAffinityChange(pParse, r3, 1);
                                        VdbeAux.VdbeAddOp2(v, OPCode.OP_IdxInsert, pExpr.iTable, r2);
                                    }
                                }
                            }
                            ReleaseTempReg(pParse, r1);
                            ReleaseTempReg(pParse, r2);
                        }
                        if (!isRowid)
                        {
                            VdbeAux.VdbeChangeP4(v, addr, keyInfo, P4Type.P4_KEYINFO);
                        }
                        break;
                    }

                case TokenKeyword.TK_EXISTS:
                case TokenKeyword.TK_SELECT:
                default:
                    {
                        /* If this has to be a scalar SELECT.  Generate code to put the
            ** value of this select in a memory cell and record the number
            ** of the memory cell in iColumn.  If this is an EXISTS, write
            ** an integer 0 (not exists) or 1 (exists) into a memory cell
            ** and record that memory cell in iColumn.
            */
                        var one = new Token("1", 1); /* Token for literal value 1 */
                        Select pSel; /* SELECT statement to encode */
                        var dest = new SelectDest(); /* How to deal with SELECt result */

                        UnitTest.TestCase(pExpr.op == TokenKeyword.TK_EXISTS);
                        UnitTest.TestCase(pExpr.op == TokenKeyword.TK_SELECT);
                        Debug.Assert(pExpr.op == TokenKeyword.TK_EXISTS || pExpr.op == TokenKeyword.TK_SELECT);

                        Debug.Assert(Helper.ExprHasProperty(pExpr, ExprFlag.EP_xIsSelect));
                        pSel = pExpr.x.pSelect;
                        sqlite3SelectDestInit(dest, 0, ++pParse.nMem);
                        if (pExpr.op == TokenKeyword.TK_SELECT)
                        {
                            dest.eDest = SelectResultType.SRT_Mem;
                            VdbeAux.VdbeAddOp2(v, OPCode.OP_Null, 0, dest.iParm);
#if SQLITE_DEBUG
                            VdbeComment(v, "Init subquery result");
#endif
                        }
                        else
                        {
                            dest.eDest = SelectResultType.SRT_Exists;
                            VdbeAux.VdbeAddOp2(v, OPCode.OP_Integer, 0, dest.iParm);
#if SQLITE_DEBUG
                            VdbeComment(v, "Init EXISTS result");
#endif
                        }
                        ExprDelete(pParse.db, ref pSel.pLimit);
                        pSel.pLimit = ExprHelper.PExpr(pParse, TokenKeyword.TK_INTEGER, null, null, one);
                        if (sqlite3Select(pParse, pSel, ref dest) != 0)
                        {
                            return 0;
                        }
                        rReg = dest.iParm;
                        Expr.ExprSetIrreducible(pExpr);
                        break;
                    }
            }

            if (testAddr != 0)
            {
                VdbeAux.VdbeJumpHere(v, testAddr - 1);
            }
            ExprCachePop(pParse, 1);

            return rReg;
        }
#endif
        // * SQLITE_OMIT_SUBQUERY */

#if !SQLITE_OMIT_SUBQUERY
        /*
** Generate code for an IN expression.
**
**      x IN (SELECT ...)
**      x IN (value, value, ...)
**
** The left-hand side (LHS) is a scalar expression.  The right-hand side (RHS)
** is an array of zero or more values.  The expression is true if the LHS is
** contained within the RHS.  The value of the expression is unknown (NULL)
** if the LHS is NULL or if the LHS is not contained within the RHS and the
** RHS contains one or more NULL values.
**
** This routine generates code will jump to destIfFalse if the LHS is not 
** contained within the RHS.  If due to NULLs we cannot determine if the LHS
** is contained in the RHS then jump to destIfNull.  If the LHS is contained
** within the RHS then fall through.
*/

        public static void ExprCodeIN(
            Parse pParse, /* Parsing and code generating context */
            Expr pExpr, /* The IN expression */
            int destIfFalse, /* Jump here if LHS is not contained in the RHS */
            int destIfNull /* Jump here if the results are unknown due to NULLs */
            )
        {
            int rRhsHasNull = 0; /* Register that is true if RHS contains NULL values */
            char affinity; /* Comparison affinity to use */
            int eType; /* Type of the RHS */
            int r1; /* Temporary use register */
            Vdbe v; /* Statement under construction */

            /* Compute the RHS.   After this step, the table with cursor
      ** pExpr.iTable will contains the values that make up the RHS.
      */
            v = pParse.pVdbe;
            Debug.Assert(v != null); /* OOM detected prior to this routine */
            VdbeNoopComment(v, "begin IN expr");
            eType = FindInIndex(pParse, pExpr, ref rRhsHasNull);

            /* Figure out the affinity to use to create a key from the results
      ** of the expression. affinityStr stores a static string suitable for
      ** P4 of OPCode.OP_MakeRecord.
      */
            affinity = comparisonAffinity(pExpr);

            /* Code the LHS, the <expr> from "<expr> IN (...)".
      */
            ExprCachePush(pParse);
            r1 = GetTempReg(pParse);
            ExprCode(pParse, pExpr.pLeft, r1);
            VdbeAux.VdbeAddOp2(v, OPCode.OP_IsNull, r1, destIfNull);


            if (eType == Const.IN_INDEX_ROWID)
            {
                /* In this case, the RHS is the ROWID of table b-tree
        */
                VdbeAux.VdbeAddOp2(v, OPCode.OP_MustBeInt, r1, destIfFalse);
                VdbeAux.VdbeAddOp3(v, OPCode.OP_NotExists, pExpr.iTable, destIfFalse, r1);
            }
            else
            {
                /* In this case, the RHS is an index b-tree.
        */
                VdbeAux.VdbeAddOp4(v, OPCode.OP_Affinity, r1, 1, 0, affinity, 1);

                /* If the set membership test fails, then the result of the 
        ** "x IN (...)" expression must be either 0 or NULL. If the set
        ** contains no NULL values, then the result is 0. If the set 
        ** contains one or more NULL values, then the result of the
        ** expression is also NULL.
        */
                if (rRhsHasNull == 0 || destIfFalse == destIfNull)
                {
                    /* This branch runs if it is known at compile time that the RHS
          ** cannot contain NULL values. This happens as the result
          ** of a "NOT NULL" constraint in the database schema.
          **
          ** Also run this branch if NULL is equivalent to FALSE
          ** for this particular IN operator.
          */
                    VdbeAux.VdbeAddOp4Int(v, OPCode.OP_NotFound, pExpr.iTable, destIfFalse, r1, 1);
                }
                else
                {
                    /* In this branch, the RHS of the IN might contain a NULL and
          ** the presence of a NULL on the RHS makes a difference in the
          ** outcome.
          */
                    int j1, j2, j3;

                    /* First check to see if the LHS is contained in the RHS.  If so,
          ** then the presence of NULLs in the RHS does not matter, so jump
          ** over all of the code that follows.
          */
                    j1 = VdbeAux.VdbeAddOp4Int(v, OPCode.OP_Found, pExpr.iTable, 0, r1, 1);

                    /* Here we begin generating code that runs if the LHS is not
          ** contained within the RHS.  Generate additional code that
          ** tests the RHS for NULLs.  If the RHS contains a NULL then
          ** jump to destIfNull.  If there are no NULLs in the RHS then
          ** jump to destIfFalse.
          */
                    j2 = VdbeAux.VdbeAddOp1(v, OPCode.OP_NotNull, rRhsHasNull);
                    j3 = VdbeAux.VdbeAddOp4Int(v, OPCode.OP_Found, pExpr.iTable, 0, rRhsHasNull, 1);
                    VdbeAux.VdbeAddOp2(v, OPCode.OP_Integer, -1, rRhsHasNull);
                    VdbeAux.VdbeJumpHere(v, j3);
                    VdbeAux.VdbeAddOp2(v, OPCode.OP_AddImm, rRhsHasNull, 1);
                    VdbeAux.VdbeJumpHere(v, j2);

                    /* Jump to the appropriate target depending on whether or not
          ** the RHS contains a NULL
          */
                    VdbeAux.VdbeAddOp2(v, OPCode.OP_If, rRhsHasNull, destIfNull);
                    VdbeAux.VdbeAddOp2(v, OPCode.OP_Goto, 0, destIfFalse);

                    /* The OPCode.OP_Found at the top of this branch jumps here when true, 
          ** causing the overall IN expression evaluation to fall through.
          */
                    VdbeAux.VdbeJumpHere(v, j1);
                }
            }
            ReleaseTempReg(pParse, r1);
            ExprCachePop(pParse, 1);
            VdbeComment(v, "end IN expr");
        }
#endif
        //* SQLITE_OMIT_SUBQUERY */

        /*
** Duplicate an 8-byte value
*/
        //static char *dup8bytes(Vdbe v, const char *in){
        //  char *out = sqlite3DbMallocRaw(VdbeAux.VdbeDb(v), 8);
        //  if( out ){
        //    memcpy(out, in, 8);
        //  }
        //  return out;
        //}

#if !SQLITE_OMIT_FLOATING_POINT
        /*
** Generate an instruction that will put the floating point
** value described by z[0..n-1] into register iMem.
**
** The z[] string will probably not be zero-terminated.  But the
** z[n] character is guaranteed to be something that does not look
** like the continuation of the number.
*/

        private static void codeReal(Vdbe v, string z, bool negateFlag, int iMem)
        {
            if (UnitTest.ALWAYS(!String.IsNullOrEmpty(z)))
            {
                double value = 0;
                //char *zV;
                Utility.Sqlite3AtoF(z, ref value);
                Debug.Assert(!Utility.Sqlite3IsNaN(value)); /* The new AtoF never returns NaN */
                if (negateFlag) value = -value;
                //zV = dup8bytes(v,  value);
                VdbeAux.VdbeAddOp4(v, OPCode.OP_Real, 0, iMem, 0, value, P4Type.P4_REAL);
            }
        }
#endif

        /*
** Generate an instruction that will put the integer describe by
** text z[0..n-1] into register iMem.
**
** The z[] string will probably not be zero-terminated.  But the
** z[n] character is guaranteed to be something that does not look
** like the continuation of the number.
*/

        private static void codeInteger(Parse pParse, Expr pExpr, bool negFlag, int iMem)
        {
            Vdbe v = pParse.pVdbe;
            if ((pExpr.flags & ExprFlag.EP_IntValue) != 0)
            {
                int i = pExpr.u.iValue;
                if (negFlag) i = -i;
                VdbeAux.VdbeAddOp2(v, OPCode.OP_Integer, i, iMem);
            }
            else
            {
                string z = pExpr.u.zToken;
                Debug.Assert(!String.IsNullOrEmpty(z));
                if (Utility.Sqlite3FitsIn64Bits(z, negFlag))
                {
                    long value = 0;
                    //string zV;
                    Utility.Sqlite3Atoi64(negFlag ? "-" + z : z, ref value);
                    //if ( negFlag ) value = -value;
                    //zV = dup8bytes( v, (char*)&value );
                    //VdbeAux.VdbeAddOp4( v, OPCode.OP_Int64, 0, iMem, 0, zV, P4Type.P4_INT64 );
                    VdbeAux.VdbeAddOp4(v, OPCode.OP_Int64, 0, iMem, 0, value, P4Type.P4_INT64);
                }
                else
                {
#if SQLITE_OMIT_FLOATING_POINT
Utility.Sqlite3ErrorMsg(pParse, "oversized integer: %s%s", negFlag ? "-" : "", z);
#else
                    codeReal(v, z, negFlag, iMem);
#endif
                }
            }
        }

        /*
    ** Clear a cache entry.
    */

        private static void cacheEntryClear(Parse pParse, yColCache p)
        {
            if (p.tempReg != 0)
            {
                if (pParse.nTempReg < Utility.ArraySize(pParse.aTempReg))
                {
                    pParse.aTempReg[pParse.nTempReg++] = p.iReg;
                }
                p.tempReg = 0;
            }
        }


        /*
    ** Record in the column cache that a particular column from a
    ** particular table is stored in a particular register.
    */

        public static void ExprCacheStore(Parse pParse, int iTab, int iCol, int iReg)
        {
            int i;
            int minLru;
            int idxLru;
            var p = new yColCache();

            Debug.Assert(iReg > 0); /* Register numbers are always positive */
            Debug.Assert(iCol >= -1 && iCol < 32768); /* Finite column numbers */

            /* The TestControlFlag.SQLITE_ColumnCache flag disables the column cache.  This is used
      ** for testing only - to verify that SQLite always gets the same answer
      ** with and without the column cache.
      */
            if ((pParse.db.flags & TestControlFlag.SQLITE_ColumnCache) != 0) return;

            /* First replace any existing entry.
      **
      ** Actually, the way the column cache is currently used, we are guaranteed
      ** that the object will never already be in cache.  Verify this guarantee.
      */
#if !NDEBUG
      for ( i = 0; i < Const.SQLITE_N_COLCACHE; i++ )//p=pParse.aColCache... p++)
      {
#if FALSE //* This code wold remove the entry from the cache if it existed */
p = pParse.aColCache[i];
if ( p.iReg != 0 && p.iTable == iTab && p.iColumn == iCol )
{
cacheEntryClear( pParse, p );
p.iLevel = pParse.iCacheLevel;
p.iReg = iReg;
p.lru = pParse.iCacheCnt++;
return;
}
#endif
        Debug.Assert( p.iReg == 0 || p.iTable != iTab || p.iColumn != iCol );
      }
#endif

            /* Find an empty slot and replace it */
            for (i = 0; i < Const.SQLITE_N_COLCACHE; i++) //p=pParse.aColCache... p++)
            {
                p = pParse.aColCache[i];
                if (p.iReg == 0)
                {
                    p.iLevel = pParse.iCacheLevel;
                    p.iTable = iTab;
                    p.iColumn = iCol;
                    p.iReg = iReg;
                    p.tempReg = 0;
                    p.lru = pParse.iCacheCnt++;
                    return;
                }
            }

            /* Replace the last recently used */
            minLru = 0x7fffffff;
            idxLru = -1;
            for (i = 0; i < Const.SQLITE_N_COLCACHE; i++) //p=pParse.aColCache..., p++)
            {
                p = pParse.aColCache[i];
                if (p.lru < minLru)
                {
                    idxLru = i;
                    minLru = p.lru;
                }
            }
            if (UnitTest.ALWAYS(idxLru >= 0))
            {
                p = pParse.aColCache[idxLru];
                p.iLevel = pParse.iCacheLevel;
                p.iTable = iTab;
                p.iColumn = iCol;
                p.iReg = iReg;
                p.tempReg = 0;
                p.lru = pParse.iCacheCnt++;
                return;
            }
        }

        /*
    ** Indicate that registers between iReg..iReg+nReg-1 are being overwritten.
    ** Purge the range of registers from the column cache.
    */

        public static void ExprCacheRemove(Parse pParse, int iReg, int nReg)
        {
            int i;
            int iLast = iReg + nReg - 1;
            yColCache p;
            for (i = 0; i < Const.SQLITE_N_COLCACHE; i++) //p=pParse.aColCache... p++)
            {
                p = pParse.aColCache[i];
                int r = p.iReg;
                if (r >= iReg && r <= iLast)
                {
                    cacheEntryClear(pParse, p);
                    p.iReg = 0;
                }
            }
        }

        /*
    ** Remember the current column cache context.  Any new entries added
    ** added to the column cache after this call are removed when the
    ** corresponding pop occurs.
    */

        public static void ExprCachePush(Parse pParse)
        {
            pParse.iCacheLevel++;
        }

        /*
    ** Remove from the column cache any entries that were added since the
    ** the previous N Push operations.  In other words, restore the cache
    ** to the state it was in N Pushes ago.
    */

        public static void ExprCachePop(Parse pParse, int N)
        {
            int i;
            yColCache p;
            Debug.Assert(N > 0);
            Debug.Assert(pParse.iCacheLevel >= N);
            pParse.iCacheLevel -= N;
            for (i = 0; i < Const.SQLITE_N_COLCACHE; i++) // p++)
            {
                p = pParse.aColCache[i];
                if (p.iReg != 0 && p.iLevel > pParse.iCacheLevel)
                {
                    cacheEntryClear(pParse, p);
                    p.iReg = 0;
                }
            }
        }

        /*
    ** When a cached column is reused, make sure that its register is
    ** no longer available as a temp register.  ticket #3879:  that same
    ** register might be in the cache in multiple places, so be sure to
    ** get them all.
    */

        public static void ExprCachePinRegister(Parse pParse, int iReg)
        {
            int i;
            yColCache p;
            for (i = 0; i < Const.SQLITE_N_COLCACHE; i++) //p=pParse->aColCache; i<Const.SQLITE_N_COLCACHE; i++, p++)
            {
                p = pParse.aColCache[i];
                if (p.iReg == iReg)
                {
                    p.tempReg = 0;
                }
            }
        }

        /*
    ** Generate code that will extract the iColumn-th column from
    ** table pTab and store the column value in a register.  An effort
    ** is made to store the column value in register iReg, but this is
    ** not guaranteed.  The location of the column value is returned.
    **
    ** There must be an open cursor to pTab in iTable when this routine
    ** is called.  If iColumn<0 then code is generated that extracts the rowid.
    */

        public static int ExprCodeGetColumn(
            Parse pParse, /* Parsing and code generating context */
            Table pTab, /* Description of the table we are reading from */
            int iColumn, /* Index of the table column */
            int iTable, /* The cursor pointing to the table */
            int iReg /* Store results here */
            )
        {
            Vdbe v = pParse.pVdbe;
            int i;
            yColCache p;

            for (i = 0; i < Const.SQLITE_N_COLCACHE; i++)
            {
// p=pParse.aColCache, p++
                p = pParse.aColCache[i];
                if (p.iReg > 0 && p.iTable == iTable && p.iColumn == iColumn)
                {
                    p.lru = pParse.iCacheCnt++;
                    ExprCachePinRegister(pParse, p.iReg);
                    return p.iReg;
                }
            }
            Debug.Assert(v != null);
            if (iColumn < 0)
            {
                VdbeAux.VdbeAddOp2(v, OPCode.OP_Rowid, iTable, iReg);
            }
            else if (UnitTest.ALWAYS(pTab != null))
            {
                int op = Utility.IsVirtual(pTab) ? OPCode.OP_VColumn : OPCode.OP_Column;
                VdbeAux.VdbeAddOp3(v, op, iTable, iColumn, iReg);
                sqlite3ColumnDefault(v, pTab, iColumn, iReg);
            }
            ExprCacheStore(pParse, iTable, iColumn, iReg);
            return iReg;
        }

        /*
    ** Clear all column cache entries.
    */

        public static void ExprCacheClear(Parse pParse)
        {
            int i;
            yColCache p;

            for (i = 0; i < Const.SQLITE_N_COLCACHE; i++) // p=pParse.aColCache... p++)
            {
                p = pParse.aColCache[i];
                if (p.iReg != 0)
                {
                    cacheEntryClear(pParse, p);
                    p.iReg = 0;
                }
            }
        }

        /*
    ** Record the fact that an affinity change has occurred on iCount
    ** registers starting with iStart.
    */

        public static void ExprCacheAffinityChange(Parse pParse, int iStart, int iCount)
        {
            ExprCacheRemove(pParse, iStart, iCount);
        }

        /*
    ** Generate code to move content from registers iFrom...iFrom+nReg-1
    ** over to iTo..iTo+nReg-1. Keep the column cache up-to-date.
    */

        public static void ExprCodeMove(Parse pParse, int iFrom, int iTo, int nReg)
        {
            int i;
            yColCache p;
            if (UnitTest.NEVER(iFrom == iTo)) return;
            VdbeAux.VdbeAddOp3(pParse.pVdbe, OPCode.OP_Move, iFrom, iTo, nReg);
            for (i = 0; i < Const.SQLITE_N_COLCACHE; i++) // p=pParse.aColCache... p++)
            {
                p = pParse.aColCache[i];
                int x = p.iReg;
                if (x >= iFrom && x < iFrom + nReg)
                {
                    p.iReg += iTo - iFrom;
                }
            }
        }

        /*
    ** Generate code to copy content from registers iFrom...iFrom+nReg-1
    ** over to iTo..iTo+nReg-1.
    */

        public static void ExprCodeCopy(Parse pParse, int iFrom, int iTo, int nReg)
        {
            int i;
            if (UnitTest.NEVER(iFrom == iTo)) return;
            for (i = 0; i < nReg; i++)
            {
                VdbeAux.VdbeAddOp2(pParse.pVdbe, OPCode.OP_Copy, iFrom + i, iTo + i);
            }
        }

#if (SQLITE_DEBUG) || (SQLITE_COVERAGE_TEST)
        /*
** Return true if any register in the range iFrom..iTo (inclusive)
** is used as part of the column cache.
**
** This routine is used within assert() and UnitTest.TestCase() macros only
** and does not appear in a normal build.
*/

        private static int usedAsColumnCache(Parse pParse, int iFrom, int iTo)
        {
            int i;
            yColCache p;
            for (i = 0; i < Const.SQLITE_N_COLCACHE; i++) //p=pParse.aColCache... p++)
            {
                p = pParse.aColCache[i];
                int r = p.iReg;
                if (r >= iFrom && r <= iTo) return 1; /*NO_TEST*/
            }
            return 0;
        }
#else
static int usedAsColumnCache( Parse pParse, int iFrom, int iTo ){return 1;}
#endif
        //* SQLITE_DEBUG || SQLITE_COVERAGE_TEST */


        /*
** If the last instruction coded is an ephemeral copy of any of
** the registers in the nReg registers beginning with iReg, then
** convert the last instruction from OPCode.OP_SCopy to OPCode.OP_Copy.
*/

        public static void ExprHardCopy(Parse pParse, int iReg, int nReg)
        {
            VdbeOp pOp;
            Vdbe v;

            //Debug.Assert( pParse.db.mallocFailed == 0 );
            v = pParse.pVdbe;
            Debug.Assert(v != null);
            pOp = sqlite3VdbeGetOp(v, -1);
            Debug.Assert(pOp != null);
            if (pOp.opcode == OPCode.OP_SCopy && pOp.p1 >= iReg && pOp.p1 < iReg + nReg)
            {
                pOp.opcode = OPCode.OP_Copy;
            }
        }

        /*
    ** Generate code to store the value of the iAlias-th alias in register
    ** target.  The first time this is called, pExpr is evaluated to compute
    ** the value of the alias.  The value is stored in an auxiliary register
    ** and the number of that register is returned.  On subsequent calls,
    ** the register number is returned without generating any code.
    **
    ** Note that in order for this to work, code must be generated in the
    ** same order that it is executed.
    **
    ** Aliases are numbered starting with 1.  So iAlias is in the range
    ** of 1 to pParse.nAlias inclusive.
    **
    ** pParse.aAlias[iAlias-1] records the register number where the value
    ** of the iAlias-th alias is stored.  If zero, that means that the
    ** alias has not yet been computed.
    */

        private static int codeAlias(Parse pParse, int iAlias, Expr pExpr, int target)
        {
#if FALSE
sqlite3 db = pParse.db;
int iReg;
if ( pParse.nAliasAlloc < pParse.nAlias )
{
pParse.aAlias = new int[pParse.nAlias]; //Malloc.DbReallocOrFree(db, pParse.aAlias,
//sizeof(pParse.aAlias[0])*pParse.nAlias );
UnitTest.TestCase( db.mallocFailed != 0 && pParse.nAliasAlloc > 0 );
if ( db.mallocFailed != 0 ) return 0;
//memset(&pParse.aAlias[pParse.nAliasAlloc], 0,
//       (pParse.nAlias-pParse.nAliasAlloc)*sizeof(pParse.aAlias[0]));
pParse.nAliasAlloc = pParse.nAlias;
}
Debug.Assert( iAlias > 0 && iAlias <= pParse.nAlias );
iReg = pParse.aAlias[iAlias - 1];
if ( iReg == 0 )
{
if ( pParse.iCacheLevel != 0 )
{
iReg = ExprHelper.ExprCodeTarget( pParse, pExpr, target );
}
else
{
iReg = ++pParse.nMem;
ExprHelper.ExprCode( pParse, pExpr, iReg );
pParse.aAlias[iAlias - 1] = iReg;
}
}
return iReg;
#else
            Helper.UNUSED_PARAMETER(iAlias);
            return ExprCodeTarget(pParse, pExpr, target);
#endif
        }

        /*
    ** Generate code into the current Vdbe to evaluate the given
    ** expression.  Attempt to store the results in register "target".
    ** Return the register where results are stored.
    **
    ** With this routine, there is no guarantee  that results will
    ** be stored in target.  The result might be stored in some other
    ** register if it is convenient to do so.  The calling function
    ** must check the return code and move the results to the desired
    ** register.
    */

        public static int ExprCodeTarget(Parse pParse, Expr pExpr, int target)
        {
            Vdbe v = pParse.pVdbe; /* The VM under construction */
            int op; /* The opcode being coded */
            int inReg = target; /* Results stored in register inReg */
            int regFree1 = 0; /* If non-zero free this temporary register */
            int regFree2 = 0; /* If non-zero free this temporary register */
            int r1 = 0, r2 = 0, r3 = 0, r4 = 0; /* Various register numbers */
            sqlite3 db = pParse.db; /* The database connection */

            Debug.Assert(target > 0 && target <= pParse.nMem);
            if (v == null)
            {
                //Debug.Assert( pParse.db.mallocFailed != 0 );
                return 0;
            }

            if (pExpr == null)
            {
                op = TokenKeyword.TK_NULL;
            }
            else
            {
                op = pExpr.op;
            }
            switch (op)
            {
                case TokenKeyword.TK_AGG_COLUMN:
                    {
                        AggInfo pAggInfo = pExpr.pAggInfo;
                        AggInfo_col pCol = pAggInfo.aCol[pExpr.iAgg];
                        if (pAggInfo.directMode == 0)
                        {
                            Debug.Assert(pCol.iMem > 0);
                            inReg = pCol.iMem;
                            break;
                        }
                        else if (pAggInfo.useSortingIdx != 0)
                        {
                            VdbeAux.VdbeAddOp3(v, OPCode.OP_Column, pAggInfo.sortingIdx,
                                              pCol.iSorterColumn, target);
                            break;
                        }
                        /* Otherwise, fall thru into the TokenKeyword.TK_COLUMN case */
                    }
                    goto case TokenKeyword.TK_COLUMN;
                case TokenKeyword.TK_COLUMN:
                    {
                        if (pExpr.iTable < 0)
                        {
                            /* This only happens when coding check constraints */
                            Debug.Assert(pParse.ckBase > 0);
                            inReg = pExpr.iColumn + pParse.ckBase;
                        }
                        else
                        {
                            inReg = ExprCodeGetColumn(pParse, pExpr.pTab,
                                                             pExpr.iColumn, pExpr.iTable, target);
                        }
                        break;
                    }
                case TokenKeyword.TK_INTEGER:
                    {
                        codeInteger(pParse, pExpr, false, target);
                        break;
                    }
#if !SQLITE_OMIT_FLOATING_POINT
                case TokenKeyword.TK_FLOAT:
                    {
                        Debug.Assert(!Helper.ExprHasProperty(pExpr, ExprFlag.EP_IntValue));
                        codeReal(v, pExpr.u.zToken, false, target);
                        break;
                    }
#endif
                case TokenKeyword.TK_STRING:
                    {
                        Debug.Assert(!Helper.ExprHasProperty(pExpr, ExprFlag.EP_IntValue));
                        VdbeAux.VdbeAddOp4(v, OPCode.OP_String8, 0, target, 0, pExpr.u.zToken, 0);
                        break;
                    }
                case TokenKeyword.TK_NULL:
                    {
                        VdbeAux.VdbeAddOp2(v, OPCode.OP_Null, 0, target);
                        break;
                    }
#if !SQLITE_OMIT_BLOB_LITERAL
                case TokenKeyword.TK_BLOB:
                    {
                        int n;
                        string z;
                        byte[] zBlob;
                        Debug.Assert(!Helper.ExprHasProperty(pExpr, ExprFlag.EP_IntValue));
                        Debug.Assert(pExpr.u.zToken[0] == 'x' || pExpr.u.zToken[0] == 'X');
                        Debug.Assert(pExpr.u.zToken[1] == '\'');
                        z = pExpr.u.zToken.Substring(2);
                        n = Utility.Sqlite3Strlen30(z) - 1;
                        Debug.Assert(z[n] == '\'');
                        zBlob = Utility.Sqlite3HexToBlob(VdbeAux.VdbeDb(v), z, n);
                        VdbeAux.VdbeAddOp4(v, OPCode.OP_Blob, n/2, target, 0, zBlob, P4Type.P4_DYNAMIC);
                        break;
                    }
#endif
                case TokenKeyword.TK_VARIABLE:
                    {
                        VdbeOp pOp;
                        Debug.Assert(!Helper.ExprHasProperty(pExpr, ExprFlag.EP_IntValue));
                        Debug.Assert(pExpr.u.zToken != null);
                        Debug.Assert(pExpr.u.zToken.Length != 0);
                        if (pExpr.u.zToken.Length == 1
                            && (pOp = sqlite3VdbeGetOp(v, -1)).opcode == OPCode.OP_Variable
                            && pOp.p1 + pOp.p3 == pExpr.iColumn
                            && pOp.p2 + pOp.p3 == target
                            && pOp.p4.z == null
                            )
                        {
                            /* If the previous instruction was a copy of the previous unnamed
              ** parameter into the previous register, then simply increment the
              ** repeat count on the prior instruction rather than making a new
              ** instruction.
              */
                            pOp.p3++;
                        }
                        else
                        {
                            VdbeAux.VdbeAddOp3(v, OPCode.OP_Variable, pExpr.iColumn, target, 1);
                            if (pExpr.u.zToken.Length > 1)
                            {
                                VdbeAux.VdbeChangeP4(v, -1, pExpr.u.zToken, 0);
                            }
                        }
                        break;
                    }
                case TokenKeyword.TK_REGISTER:
                    {
                        inReg = pExpr.iTable;
                        break;
                    }
                case TokenKeyword.TK_AS:
                    {
                        inReg = codeAlias(pParse, pExpr.iTable, pExpr.pLeft, target);
                        break;
                    }
#if !SQLITE_OMIT_CAST
                case TokenKeyword.TK_CAST:
                    {
                        /* Expressions of the form:   CAST(pLeft AS token) */
                        int aff, to_op;
                        inReg = ExprCodeTarget(pParse, pExpr.pLeft, target);
                        Debug.Assert(!Helper.ExprHasProperty(pExpr, ExprFlag.EP_IntValue));
                        aff = Build.AffinityType(pExpr.u.zToken);
                        to_op = aff - ColumnAffinityType.SQLITE_AFF_TEXT + OPCode.OP_ToText;
                        Debug.Assert(to_op == OPCode.OP_ToText || aff != ColumnAffinityType.SQLITE_AFF_TEXT);
                        Debug.Assert(to_op == OPCode.OP_ToBlob || aff != ColumnAffinityType.SQLITE_AFF_NONE);
                        Debug.Assert(to_op == OPCode.OP_ToNumeric || aff != ColumnAffinityType.SQLITE_AFF_NUMERIC);
                        Debug.Assert(to_op == OPCode.OP_ToInt || aff != ColumnAffinityType.SQLITE_AFF_INTEGER);
                        Debug.Assert(to_op == OPCode.OP_ToReal || aff != ColumnAffinityType.SQLITE_AFF_REAL);
                        UnitTest.TestCase(to_op == OPCode.OP_ToText);
                        UnitTest.TestCase(to_op == OPCode.OP_ToBlob);
                        UnitTest.TestCase(to_op == OPCode.OP_ToNumeric);
                        UnitTest.TestCase(to_op == OPCode.OP_ToInt);
                        UnitTest.TestCase(to_op == OPCode.OP_ToReal);
                        if (inReg != target)
                        {
                            VdbeAux.VdbeAddOp2(v, OPCode.OP_SCopy, inReg, target);
                            inReg = target;
                        }
                        VdbeAux.VdbeAddOp1(v, to_op, inReg);
                        UnitTest.TestCase(usedAsColumnCache(pParse, inReg, inReg) != 0);
                        ExprCacheAffinityChange(pParse, inReg, 1);
                        break;
                    }
#endif
                    // * SQLITE_OMIT_CAST */
                case TokenKeyword.TK_LT:
                case TokenKeyword.TK_LE:
                case TokenKeyword.TK_GT:
                case TokenKeyword.TK_GE:
                case TokenKeyword.TK_NE:
                case TokenKeyword.TK_EQ:
                    {
                        Debug.Assert(TokenKeyword.TK_LT == OPCode.OP_Lt);
                        Debug.Assert(TokenKeyword.TK_LE == OPCode.OP_Le);
                        Debug.Assert(TokenKeyword.TK_GT == OPCode.OP_Gt);
                        Debug.Assert(TokenKeyword.TK_GE == OPCode.OP_Ge);
                        Debug.Assert(TokenKeyword.TK_EQ == OPCode.OP_Eq);
                        Debug.Assert(TokenKeyword.TK_NE == OPCode.OP_Ne);
                        UnitTest.TestCase(op == TokenKeyword.TK_LT);
                        UnitTest.TestCase(op == TokenKeyword.TK_LE);
                        UnitTest.TestCase(op == TokenKeyword.TK_GT);
                        UnitTest.TestCase(op == TokenKeyword.TK_GE);
                        UnitTest.TestCase(op == TokenKeyword.TK_EQ);
                        UnitTest.TestCase(op == TokenKeyword.TK_NE);
                        r1 = ExprCodeTemp(pParse, pExpr.pLeft, ref regFree1);
                        r2 = ExprCodeTemp(pParse, pExpr.pRight, ref regFree2);
                        codeCompare(pParse, pExpr.pLeft, pExpr.pRight, op,
                                    r1, r2, inReg, Const.SQLITE_STOREP2);
                        UnitTest.TestCase(regFree1 == 0);
                        UnitTest.TestCase(regFree2 == 0);
                        break;
                    }
                case TokenKeyword.TK_IS:
                case TokenKeyword.TK_ISNOT:
                    {
                        UnitTest.TestCase(op == TokenKeyword.TK_IS);
                        UnitTest.TestCase(op == TokenKeyword.TK_ISNOT);
                        r1 = ExprCodeTemp(pParse, pExpr.pLeft, ref regFree1);
                        r2 = ExprCodeTemp(pParse, pExpr.pRight, ref regFree2);
                        op = (op == TokenKeyword.TK_IS) ? TokenKeyword.TK_EQ : TokenKeyword.TK_NE;
                        codeCompare(pParse, pExpr.pLeft, pExpr.pRight, op,
                                    r1, r2, inReg, Const.SQLITE_STOREP2 | Const.SQLITE_NULLEQ);
                        UnitTest.TestCase(regFree1 == 0);
                        UnitTest.TestCase(regFree2 == 0);
                        break;
                    }
                case TokenKeyword.TK_AND:
                case TokenKeyword.TK_OR:
                case TokenKeyword.TK_PLUS:
                case TokenKeyword.TK_STAR:
                case TokenKeyword.TK_MINUS:
                case TokenKeyword.TK_REM:
                case TokenKeyword.TK_BITAND:
                case TokenKeyword.TK_BITOR:
                case TokenKeyword.TK_SLASH:
                case TokenKeyword.TK_LSHIFT:
                case TokenKeyword.TK_RSHIFT:
                case TokenKeyword.TK_CONCAT:
                    {
                        Debug.Assert(TokenKeyword.TK_AND == OPCode.OP_And);
                        Debug.Assert(TokenKeyword.TK_OR == OPCode.OP_Or);
                        Debug.Assert(TokenKeyword.TK_PLUS == OPCode.OP_Add);
                        Debug.Assert(TokenKeyword.TK_MINUS == OPCode.OP_Subtract);
                        Debug.Assert(TokenKeyword.TK_REM == OPCode.OP_Remainder);
                        Debug.Assert(TokenKeyword.TK_BITAND == OPCode.OP_BitAnd);
                        Debug.Assert(TokenKeyword.TK_BITOR == OPCode.OP_BitOr);
                        Debug.Assert(TokenKeyword.TK_SLASH == OPCode.OP_Divide);
                        Debug.Assert(TokenKeyword.TK_LSHIFT == OPCode.OP_ShiftLeft);
                        Debug.Assert(TokenKeyword.TK_RSHIFT == OPCode.OP_ShiftRight);
                        Debug.Assert(TokenKeyword.TK_CONCAT == OPCode.OP_Concat);
                        UnitTest.TestCase(op == TokenKeyword.TK_AND);
                        UnitTest.TestCase(op == TokenKeyword.TK_OR);
                        UnitTest.TestCase(op == TokenKeyword.TK_PLUS);
                        UnitTest.TestCase(op == TokenKeyword.TK_MINUS);
                        UnitTest.TestCase(op == TokenKeyword.TK_REM);
                        UnitTest.TestCase(op == TokenKeyword.TK_BITAND);
                        UnitTest.TestCase(op == TokenKeyword.TK_BITOR);
                        UnitTest.TestCase(op == TokenKeyword.TK_SLASH);
                        UnitTest.TestCase(op == TokenKeyword.TK_LSHIFT);
                        UnitTest.TestCase(op == TokenKeyword.TK_RSHIFT);
                        UnitTest.TestCase(op == TokenKeyword.TK_CONCAT);
                        r1 = ExprCodeTemp(pParse, pExpr.pLeft, ref regFree1);
                        r2 = ExprCodeTemp(pParse, pExpr.pRight, ref regFree2);
                        VdbeAux.VdbeAddOp3(v, op, r2, r1, target);
                        UnitTest.TestCase(regFree1 == 0);
                        UnitTest.TestCase(regFree2 == 0);
                        break;
                    }
                case TokenKeyword.TK_UMINUS:
                    {
                        Expr pLeft = pExpr.pLeft;
                        Debug.Assert(pLeft != null);
                        if (pLeft.op == TokenKeyword.TK_INTEGER)
                        {
                            codeInteger(pParse, pLeft, true, target);
#if !SQLITE_OMIT_FLOATING_POINT
                        }
                        else if (pLeft.op == TokenKeyword.TK_FLOAT)
                        {
                            Debug.Assert(!Helper.ExprHasProperty(pExpr, ExprFlag.EP_IntValue));
                            codeReal(v, pLeft.u.zToken, true, target);
#endif
                        }
                        else
                        {
                            regFree1 = r1 = GetTempReg(pParse);
                            VdbeAux.VdbeAddOp2(v, OPCode.OP_Integer, 0, r1);
                            r2 = ExprCodeTemp(pParse, pExpr.pLeft, ref regFree2);
                            VdbeAux.VdbeAddOp3(v, OPCode.OP_Subtract, r2, r1, target);
                            UnitTest.TestCase(regFree2 == 0);
                        }
                        inReg = target;
                        break;
                    }
                case TokenKeyword.TK_BITNOT:
                case TokenKeyword.TK_NOT:
                    {
                        Debug.Assert(TokenKeyword.TK_BITNOT == OPCode.OP_BitNot);
                        Debug.Assert(TokenKeyword.TK_NOT == OPCode.OP_Not);
                        UnitTest.TestCase(op == TokenKeyword.TK_BITNOT);
                        UnitTest.TestCase(op == TokenKeyword.TK_NOT);
                        r1 = ExprCodeTemp(pParse, pExpr.pLeft, ref regFree1);
                        UnitTest.TestCase(regFree1 == 0);
                        inReg = target;
                        VdbeAux.VdbeAddOp2(v, op, r1, inReg);
                        break;
                    }
                case TokenKeyword.TK_ISNULL:
                case TokenKeyword.TK_NOTNULL:
                    {
                        int addr;
                        Debug.Assert(TokenKeyword.TK_ISNULL == OPCode.OP_IsNull);
                        Debug.Assert(TokenKeyword.TK_NOTNULL == OPCode.OP_NotNull);
                        UnitTest.TestCase(op == TokenKeyword.TK_ISNULL);
                        UnitTest.TestCase(op == TokenKeyword.TK_NOTNULL);
                        VdbeAux.VdbeAddOp2(v, OPCode.OP_Integer, 1, target);
                        r1 = ExprCodeTemp(pParse, pExpr.pLeft, ref regFree1);
                        UnitTest.TestCase(regFree1 == 0);
                        addr = VdbeAux.VdbeAddOp1(v, op, r1);
                        VdbeAux.VdbeAddOp2(v, OPCode.OP_AddImm, target, -1);
                        VdbeAux.VdbeJumpHere(v, addr);
                        break;
                    }
                case TokenKeyword.TK_AGG_FUNCTION:
                    {
                        AggInfo pInfo = pExpr.pAggInfo;
                        if (pInfo == null)
                        {
                            Debug.Assert(!Helper.ExprHasProperty(pExpr, ExprFlag.EP_IntValue));
                            Utility.Sqlite3ErrorMsg(pParse, "misuse of aggregate: %s()", pExpr.u.zToken);
                        }
                        else
                        {
                            inReg = pInfo.aFunc[pExpr.iAgg].iMem;
                        }
                        break;
                    }
                case TokenKeyword.TK_CONST_FUNC:
                case TokenKeyword.TK_FUNCTION:
                    {
                        ExprList pFarg; /* List of function arguments */
                        int nFarg; /* Number of function arguments */
                        FuncDef pDef; /* The function definition object */
                        int nId; /* Length of the function name in bytes */
                        string zId; /* The function name */
                        int constMask = 0; /* Mask of function arguments that are constant */
                        int i; /* Loop counter */
                        byte enc = Helper.ENC(db); /* The text encoding used by this database */
                        CollSeq pColl = null; /* A collating sequence */

                        Debug.Assert(!Helper.ExprHasProperty(pExpr, ExprFlag.EP_xIsSelect));
                        UnitTest.TestCase(op == TokenKeyword.TK_CONST_FUNC);
                        UnitTest.TestCase(op == TokenKeyword.TK_FUNCTION);
                        if (Helper.ExprHasAnyProperty(pExpr, ExprFlag.EP_TokenOnly))
                        {
                            pFarg = null;
                        }
                        else
                        {
                            pFarg = pExpr.x.pList;
                        }
                        nFarg = pFarg != null ? pFarg.nExpr : 0;
                        Debug.Assert(!Helper.ExprHasProperty(pExpr, ExprFlag.EP_IntValue));
                        zId = pExpr.u.zToken;
                        nId = Utility.Sqlite3Strlen30(zId);
                        pDef = Callback.FindFunction(pParse.db, zId, nId, nFarg, enc, 0);
                        if (pDef == null)
                        {
                            Utility.Sqlite3ErrorMsg(pParse, "unknown function: %.*s()", nId, zId);
                            break;
                        }

                        /* Attempt a direct implementation of the built-in COALESCE() and
            ** IFNULL() functions.  This avoids unnecessary evalation of
            ** arguments past the first non-NULL argument.
            */
                        if ((pDef.flags & FuncDefFlag.SQLITE_FUNC_COALESCE) != 0)
                        {
                            int endCoalesce = VdbeAux.VdbeMakeLabel(v);
                            Debug.Assert(nFarg >= 2);
                            ExprCode(pParse, pFarg.a[0].pExpr, target);
                            for (i = 1; i < nFarg; i++)
                            {
                                VdbeAux.VdbeAddOp2(v, OPCode.OP_NotNull, target, endCoalesce);
                                ExprCacheRemove(pParse, target, 1);
                                ExprCachePush(pParse);
                                ExprCode(pParse, pFarg.a[i].pExpr, target);
                                ExprCachePop(pParse, 1);
                            }
                            VdbeAux.VdbeResolveLabel(v, endCoalesce);
                            break;
                        }

                        if (pFarg != null)
                        {
                            r1 = GetTempRange(pParse, nFarg);
                            ExprCachePush(pParse); /* Ticket 2ea2425d34be */
                            ExprCodeExprList(pParse, pFarg, r1, true);
                            ExprCachePop(pParse, 1); /* Ticket 2ea2425d34be */
                        }
                        else
                        {
                            r1 = 0;
                        }
#if !SQLITE_OMIT_VIRTUALTABLE
/* Possibly overload the function if the first argument is
** a virtual table column.
**
** For infix functions (LIKE, GLOB, REGEXP, and MATCH) use the
** second argument, not the first, as the argument to test to
** see if it is a column in a virtual table.  This is done because
** the left operand of infix functions (the operand we want to
** control overloading) ends up as the second argument to the
** function.  The expression "A glob B" is equivalent to
** "glob(B,A).  We want to use the A in "A glob B" to test
** for function overloading.  But we use the B term in "glob(B,A)".
*/
if ( nFarg >= 2 && ( pExpr.flags & ExprFlag.EP_InfixFunc ) )
{
pDef = sqlite3VtabOverloadFunction( db, pDef, nFarg, pFarg.a[1].pExpr );
}
else if ( nFarg > 0 )
{
pDef = sqlite3VtabOverloadFunction( db, pDef, nFarg, pFarg.a[0].pExpr );
}
#endif
                        for (i = 0; i < nFarg; i++)
                        {
                            if (i < 32 && ExprIsConstant(pFarg.a[i].pExpr) != 0)
                            {
                                constMask |= (1 << i);
                            }
                            if ((pDef.flags & FuncDefFlag.SQLITE_FUNC_NEEDCOLL) != 0 && null == pColl)
                            {
                                pColl = ExprCollSeq(pParse, pFarg.a[i].pExpr);
                            }
                        }
                        if ((pDef.flags & FuncDefFlag.SQLITE_FUNC_NEEDCOLL) != 0)
                        {
                            if (null == pColl) pColl = db.pDfltColl;
                            VdbeAux.VdbeAddOp4(v, OPCode.OP_CollSeq, 0, 0, 0, pColl, P4Type.P4_COLLSEQ);
                        }
                        VdbeAux.VdbeAddOp4(v, OPCode.OP_Function, constMask, r1, target,
                                          pDef, P4Type.P4_FUNCDEF);
                        VdbeAux.VdbeChangeP5(v, (byte) nFarg);
                        if (nFarg != 0)
                        {
                            ReleaseTempRange(pParse, r1, nFarg);
                        }
                        break;
                    }
#if !SQLITE_OMIT_SUBQUERY
                case TokenKeyword.TK_EXISTS:
                case TokenKeyword.TK_SELECT:
                    {
                        UnitTest.TestCase(op == TokenKeyword.TK_EXISTS);
                        UnitTest.TestCase(op == TokenKeyword.TK_SELECT);
                        inReg = CodeSubselect(pParse, pExpr, 0, false);
                        break;
                    }
                case TokenKeyword.TK_IN:
                    {
                        int destIfFalse = VdbeAux.VdbeMakeLabel(v);
                        int destIfNull = VdbeAux.VdbeMakeLabel(v);
                        VdbeAux.VdbeAddOp2(v, OPCode.OP_Null, 0, target);
                        ExprCodeIN(pParse, pExpr, destIfFalse, destIfNull);
                        VdbeAux.VdbeAddOp2(v, OPCode.OP_Integer, 1, target);
                        VdbeAux.VdbeResolveLabel(v, destIfFalse);
                        VdbeAux.VdbeAddOp2(v, OPCode.OP_AddImm, target, 0);
                        VdbeAux.VdbeResolveLabel(v, destIfNull);
                        break;
                    }
#endif
                    //* SQLITE_OMIT_SUBQUERY */

                    /*
**    x BETWEEN y AND z
**
** This is equivalent to
**
**    x>=y AND x<=z
**
** X is stored in pExpr.pLeft.
** Y is stored in pExpr.x.pList.a[0].pExpr.
** Z is stored in pExpr.x.pList.a[1].pExpr.
*/
                case TokenKeyword.TK_BETWEEN:
                    {
                        Expr pLeft = pExpr.pLeft;
                        ExprList_item pLItem = pExpr.x.pList.a[0];
                        Expr pRight = pLItem.pExpr;
                        r1 = ExprCodeTemp(pParse, pLeft, ref regFree1);
                        r2 = ExprCodeTemp(pParse, pRight, ref regFree2);
                        UnitTest.TestCase(regFree1 == 0);
                        UnitTest.TestCase(regFree2 == 0);
                        r3 = GetTempReg(pParse);
                        r4 = GetTempReg(pParse);
                        codeCompare(pParse, pLeft, pRight, OPCode.OP_Ge,
                                    r1, r2, r3, Const.SQLITE_STOREP2);
                        pLItem = pExpr.x.pList.a[1]; // pLItem++;
                        pRight = pLItem.pExpr;
                        ReleaseTempReg(pParse, regFree2);
                        r2 = ExprCodeTemp(pParse, pRight, ref regFree2);
                        UnitTest.TestCase(regFree2 == 0);
                        codeCompare(pParse, pLeft, pRight, OPCode.OP_Le, r1, r2, r4, Const.SQLITE_STOREP2);
                        VdbeAux.VdbeAddOp3(v, OPCode.OP_And, r3, r4, target);
                        ReleaseTempReg(pParse, r3);
                        ReleaseTempReg(pParse, r4);
                        break;
                    }
                case TokenKeyword.TK_UPLUS:
                    {
                        inReg = ExprCodeTarget(pParse, pExpr.pLeft, target);
                        break;
                    }
                case TokenKeyword.TK_TRIGGER:
                    {
                        /* If the opcode is TokenKeyword.TK_TRIGGER, then the expression is a reference
            ** to a column in the new.* or old.* pseudo-tables available to
            ** trigger programs. In this case Expr.iTable is set to 1 for the
            ** new.* pseudo-table, or 0 for the old.* pseudo-table. Expr.iColumn
            ** is set to the column of the pseudo-table to read, or to -1 to
            ** read the rowid field.
            **
            ** The expression is implemented using an OPCode.OP_Param opcode. The p1
            ** parameter is set to 0 for an old.rowid reference, or to (i+1)
            ** to reference another column of the old.* pseudo-table, where 
            ** i is the index of the column. For a new.rowid reference, p1 is
            ** set to (n+1), where n is the number of columns in each pseudo-table.
            ** For a reference to any other column in the new.* pseudo-table, p1
            ** is set to (n+2+i), where n and i are as defined previously. For
            ** example, if the table on which triggers are being fired is
            ** declared as:
            **
            **   CREATE TABLE t1(a, b);
            **
            ** Then p1 is interpreted as follows:
            **
            **   p1==0   .    old.rowid     p1==3   .    new.rowid
            **   p1==1   .    old.a         p1==4   .    new.a
            **   p1==2   .    old.b         p1==5   .    new.b       
            */
                        Table pTab = pExpr.pTab;
                        int p1 = pExpr.iTable*(pTab.nCol + 1) + 1 + pExpr.iColumn;

                        Debug.Assert(pExpr.iTable == 0 || pExpr.iTable == 1);
                        Debug.Assert(pExpr.iColumn >= -1 && pExpr.iColumn < pTab.nCol);
                        Debug.Assert(pTab.iPKey < 0 || pExpr.iColumn != pTab.iPKey);
                        Debug.Assert(p1 >= 0 && p1 < (pTab.nCol*2 + 2));

                        VdbeAux.VdbeAddOp2(v, OPCode.OP_Param, p1, target);
                        VdbeComment(v, "%s.%s -> $%d",
                                    (pExpr.iTable != 0 ? "new" : "old"),
                                    (pExpr.iColumn < 0 ? "rowid" : pExpr.pTab.aCol[pExpr.iColumn].zName),
                                    target
                            );

                        /* If the column has REAL affinity, it may currently be stored as an
            ** integer. Use OPCode.OP_RealAffinity to make sure it is really real.  */
                        if (pExpr.iColumn >= 0
                            && pTab.aCol[pExpr.iColumn].affinity == ColumnAffinityType.SQLITE_AFF_REAL
                            )
                        {
                            VdbeAux.VdbeAddOp1(v, OPCode.OP_RealAffinity, target);
                        }
                        break;
                    }

                    /*
        ** Form A:
        **   CASE x WHEN e1 THEN r1 WHEN e2 THEN r2 ... WHEN eN THEN rN ELSE y END
        **
        ** Form B:
        **   CASE WHEN e1 THEN r1 WHEN e2 THEN r2 ... WHEN eN THEN rN ELSE y END
        **
        ** Form A is can be transformed into the equivalent form B as follows:
        **   CASE WHEN x=e1 THEN r1 WHEN x=e2 THEN r2 ...
        **        WHEN x=eN THEN rN ELSE y END
        **
        ** X (if it exists) is in pExpr.pLeft.
        ** Y is in pExpr.pRight.  The Y is also optional.  If there is no
        ** ELSE clause and no other term matches, then the result of the
        ** exprssion is NULL.
        ** Ei is in pExpr.x.pList.a[i*2] and Ri is pExpr.x.pList.a[i*2+1].
        **
        ** The result of the expression is the Ri for the first matching Ei,
        ** or if there is no matching Ei, the ELSE term Y, or if there is
        ** no ELSE term, NULL.
        */
                default:
                    {
                        Debug.Assert(op == TokenKeyword.TK_CASE);
                        int endLabel; /* GOTO label for end of CASE stmt */
                        int nextCase; /* GOTO label for next WHEN clause */
                        int nExpr; /* 2x number of WHEN terms */
                        int i; /* Loop counter */
                        ExprList pEList; /* List of WHEN terms */
                        ExprList_item[] aListelem; /* Array of WHEN terms */
                        var opCompare = new Expr(); /* The X==Ei expression */
                        Expr cacheX; /* Cached expression X */
                        Expr pX; /* The X expression */
                        Expr pTest = null; /* X==Ei (form A) or just Ei (form B) */
#if !NDEBUG
            int iCacheLevel = pParse.iCacheLevel;
            //VVA_ONLY( int iCacheLevel = pParse.iCacheLevel; )
#endif
                        Debug.Assert(!Helper.ExprHasProperty(pExpr, ExprFlag.EP_xIsSelect) && pExpr.x.pList != null);
                        Debug.Assert((pExpr.x.pList.nExpr%2) == 0);
                        Debug.Assert(pExpr.x.pList.nExpr > 0);
                        pEList = pExpr.x.pList;
                        aListelem = pEList.a;
                        nExpr = pEList.nExpr;
                        endLabel = VdbeAux.VdbeMakeLabel(v);
                        if ((pX = pExpr.pLeft) != null)
                        {
                            cacheX = pX;
                            UnitTest.TestCase(pX.op == TokenKeyword.TK_COLUMN);
                            UnitTest.TestCase(pX.op == TokenKeyword.TK_REGISTER);
                            cacheX.iTable = ExprCodeTemp(pParse, pX, ref regFree1);
                            UnitTest.TestCase(regFree1 == 0);
                            cacheX.op = TokenKeyword.TK_REGISTER;
                            opCompare.op = TokenKeyword.TK_EQ;
                            opCompare.pLeft = cacheX;
                            pTest = opCompare;
                        }
                        for (i = 0; i < nExpr; i = i + 2)
                        {
                            ExprCachePush(pParse);
                            if (pX != null)
                            {
                                Debug.Assert(pTest != null);
                                opCompare.pRight = aListelem[i].pExpr;
                            }
                            else
                            {
                                pTest = aListelem[i].pExpr;
                            }
                            nextCase = VdbeAux.VdbeMakeLabel(v);
                            UnitTest.TestCase(pTest.op == TokenKeyword.TK_COLUMN);
                            ExprIfFalse(pParse, pTest, nextCase, Const.SQLITE_JUMPIFNULL);
                            UnitTest.TestCase(aListelem[i + 1].pExpr.op == TokenKeyword.TK_COLUMN);
                            UnitTest.TestCase(aListelem[i + 1].pExpr.op == TokenKeyword.TK_REGISTER);
                            ExprCode(pParse, aListelem[i + 1].pExpr, target);
                            VdbeAux.VdbeAddOp2(v, OPCode.OP_Goto, 0, endLabel);
                            ExprCachePop(pParse, 1);
                            VdbeAux.VdbeResolveLabel(v, nextCase);
                        }
                        if (pExpr.pRight != null)
                        {
                            ExprCachePush(pParse);
                            ExprCode(pParse, pExpr.pRight, target);
                            ExprCachePop(pParse, 1);
                        }
                        else
                        {
                            VdbeAux.VdbeAddOp2(v, OPCode.OP_Null, 0, target);
                        }
#if !NDEBUG
            Debug.Assert( /* db.mallocFailed != 0 || */ pParse.nErr > 0
            || pParse.iCacheLevel == iCacheLevel );
#endif
                        VdbeAux.VdbeResolveLabel(v, endLabel);
                        break;
                    }
#if !SQLITE_OMIT_TRIGGER
                case TokenKeyword.TK_RAISE:
                    {
                        Debug.Assert(pExpr.affinity == OnConstraintError.OE_Rollback
                                     || pExpr.affinity == OnConstraintError.OE_Abort
                                     || pExpr.affinity == OnConstraintError.OE_Fail
                                     || pExpr.affinity == OnConstraintError.OE_Ignore
                            );
                        if (null == pParse.pTriggerTab)
                        {
                            Utility.Sqlite3ErrorMsg(pParse,
                                            "RAISE() may only be used within a trigger-program");
                            return 0;
                        }
                        if (pExpr.affinity == OnConstraintError.OE_Abort)
                        {
                            Build.MayAbort(pParse);
                        }
                        Debug.Assert(!Helper.ExprHasProperty(pExpr, ExprFlag.EP_IntValue));
                        if (pExpr.affinity == OnConstraintError.OE_Ignore)
                        {
                            VdbeAux.VdbeAddOp4(
                                v, OPCode.OP_Halt, StatusCode.SQLITE_OK, OnConstraintError.OE_Ignore, 0, pExpr.u.zToken, 0);
                        }
                        else
                        {
                            Build.HaltConstraint(pParse, pExpr.affinity, pExpr.u.zToken, 0);
                        }

                        break;
                    }
#endif
            }
            ReleaseTempReg(pParse, regFree1);
            ReleaseTempReg(pParse, regFree2);
            return inReg;
        }

        /*
    ** Generate code to evaluate an expression and store the results
    ** into a register.  Return the register number where the results
    ** are stored.
    **
    ** If the register is a temporary register that can be deallocated,
    ** then write its number into pReg.  If the result register is not
    ** a temporary, then set pReg to zero.
    */

        public static int ExprCodeTemp(Parse pParse, Expr pExpr, ref int pReg)
        {
            int r1 = GetTempReg(pParse);
            int r2 = ExprCodeTarget(pParse, pExpr, r1);
            if (r2 == r1)
            {
                pReg = r1;
            }
            else
            {
                ReleaseTempReg(pParse, r1);
                pReg = 0;
            }
            return r2;
        }

        /*
    ** Generate code that will evaluate expression pExpr and store the
    ** results in register target.  The results are guaranteed to appear
    ** in register target.
    */

        public static int ExprCode(Parse pParse, Expr pExpr, int target)
        {
            int inReg;

            Debug.Assert(target > 0 && target <= pParse.nMem);
            inReg = ExprCodeTarget(pParse, pExpr, target);
            Debug.Assert(pParse.pVdbe != null /* || pParse.db.mallocFailed != 0 */);
            if (inReg != target && pParse.pVdbe != null)
            {
                VdbeAux.VdbeAddOp2(pParse.pVdbe, OPCode.OP_SCopy, inReg, target);
            }
            return target;
        }

        /*
    ** Generate code that evalutes the given expression and puts the result
    ** in register target.
    **
    ** Also make a copy of the expression results into another "cache" register
    ** and modify the expression so that the next time it is evaluated,
    ** the result is a copy of the cache register.
    **
    ** This routine is used for expressions that are used multiple
    ** times.  They are evaluated once and the results of the expression
    ** are reused.
    */

        public static int ExprCodeAndCache(Parse pParse, Expr pExpr, int target)
        {
            Vdbe v = pParse.pVdbe;
            int inReg;
            inReg = ExprCode(pParse, pExpr, target);
            Debug.Assert(target > 0);
            /* This routine is called for terms to INSERT or UPDATE.  And the only
      ** other place where expressions can be converted into TokenKeyword.TK_REGISTER is
      ** in WHERE clause processing.  So as currently implemented, there is
      ** no way for a TokenKeyword.TK_REGISTER to exist here.  But it seems prudent to
      ** keep the UnitTest.ALWAYS() in case the conditions above change with future
      ** modifications or enhancements. */
            if (UnitTest.ALWAYS(pExpr.op != TokenKeyword.TK_REGISTER))
            {
                int iMem;
                iMem = ++pParse.nMem;
                VdbeAux.VdbeAddOp2(v, OPCode.OP_Copy, inReg, iMem);
                pExpr.iTable = iMem;
                pExpr.op2 = pExpr.op;
                pExpr.op = TokenKeyword.TK_REGISTER;
            }
            return inReg;
        }

        /*
    ** Return TRUE if pExpr is an constant expression that is appropriate
    ** for factoring out of a loop.  Appropriate expressions are:
    **
    **    *  Any expression that evaluates to two or more opcodes.
    **
    **    *  Any OPCode.OP_Integer, OPCode.OP_Real, OPCode.OP_String, OPCode.OP_Blob, OPCode.OP_Null,
    **       or OPCode.OP_Variable that does not need to be placed in a
    **       specific register.
    **
    ** There is no point in factoring out single-instruction constant
    ** expressions that need to be placed in a particular register.
    ** We could factor them out, but then we would end up adding an
    ** OPCode.OP_SCopy instruction to move the value into the correct register
    ** later.  We might as well just use the original instruction and
    ** avoid the OPCode.OP_SCopy.
    */

        private static int isAppropriateForFactoring(Expr p)
        {
            if (ExprIsConstantNotJoin(p) == 0)
            {
                return 0; /* Only constant expressions are appropriate for factoring */
            }
            if ((p.flags & ExprFlag.EP_FixedDest) == 0)
            {
                return 1; /* Any constant without a fixed destination is appropriate */
            }
            while (p.op == TokenKeyword.TK_UPLUS) p = p.pLeft;
            switch (p.op)
            {
#if !SQLITE_OMIT_BLOB_LITERAL
                case TokenKeyword.TK_BLOB:
#endif
                case TokenKeyword.TK_VARIABLE:
                case TokenKeyword.TK_INTEGER:
                case TokenKeyword.TK_FLOAT:
                case TokenKeyword.TK_NULL:
                case TokenKeyword.TK_STRING:
                    {
                        UnitTest.TestCase(p.op == TokenKeyword.TK_BLOB);
                        UnitTest.TestCase(p.op == TokenKeyword.TK_VARIABLE);
                        UnitTest.TestCase(p.op == TokenKeyword.TK_INTEGER);
                        UnitTest.TestCase(p.op == TokenKeyword.TK_FLOAT);
                        UnitTest.TestCase(p.op == TokenKeyword.TK_NULL);
                        UnitTest.TestCase(p.op == TokenKeyword.TK_STRING);
                        /* Single-instruction constants with a fixed destination are
            ** better done in-line.  If we factor them, they will just end
            ** up generating an OPCode.OP_SCopy to move the value to the destination
            ** register. */
                        return 0;
                    }
                case TokenKeyword.TK_UMINUS:
                    {
                        if (p.pLeft.op == TokenKeyword.TK_FLOAT || p.pLeft.op == TokenKeyword.TK_INTEGER)
                        {
                            return 0;
                        }
                        break;
                    }
                default:
                    {
                        break;
                    }
            }
            return 1;
        }

        /*
    ** If pExpr is a constant expression that is appropriate for
    ** factoring out of a loop, then evaluate the expression
    ** into a register and convert the expression into a TokenKeyword.TK_REGISTER
    ** expression.
    */

        private static int evalConstExpr(Walker pWalker, ref Expr pExpr)
        {
            Parse pParse = pWalker.pParse;
            switch (pExpr.op)
            {
                case TokenKeyword.TK_IN:
                case TokenKeyword.TK_REGISTER:
                    {
                        return Wrc.WRC_Prune;
                    }
                case TokenKeyword.TK_FUNCTION:
                case TokenKeyword.TK_AGG_FUNCTION:
                case TokenKeyword.TK_CONST_FUNC:
                    {
                        /* The arguments to a function have a fixed destination.
            ** Mark them this way to avoid generated unneeded OPCode.OP_SCopy
            ** instructions.
            */
                        ExprList pList = pExpr.x.pList;
                        Debug.Assert(!Helper.ExprHasProperty(pExpr, ExprFlag.EP_xIsSelect));
                        if (pList != null)
                        {
                            int i = pList.nExpr;
                            ExprList_item pItem; //= pList.a;
                            for (; i > 0; i--)
                            {
//, pItem++){
                                pItem = pList.a[pList.nExpr - i];
                                if (UnitTest.ALWAYS(pItem.pExpr != null)) pItem.pExpr.flags |= ExprFlag.EP_FixedDest;
                            }
                        }
                        break;
                    }
            }
            if (isAppropriateForFactoring(pExpr) != 0)
            {
                int r1 = ++pParse.nMem;
                int r2;
                r2 = ExprCodeTarget(pParse, pExpr, r1);
                if (UnitTest.NEVER(r1 != r2)) ReleaseTempReg(pParse, r1);
                pExpr.op2 = pExpr.op;
                pExpr.op = TokenKeyword.TK_REGISTER;
                pExpr.iTable = r2;
                return Wrc.WRC_Prune;
            }
            return Wrc.WRC_Continue;
        }

        /*
    ** Preevaluate constant subexpressions within pExpr and store the
    ** results in registers.  Modify pExpr so that the constant subexpresions
    ** are TokenKeyword.TK_REGISTER opcodes that refer to the precomputed values.
    */

        public static void ExprCodeConstants(Parse pParse, Expr pExpr)
        {
            var w = new Walker();
            w.xExprCallback = evalConstExpr;
            w.xSelectCallback = null;
            w.pParse = pParse;
            sqlite3WalkExpr(w, ref pExpr);
        }

        /*
    ** Generate code that pushes the value of every element of the given
    ** expression list into a sequence of registers beginning at target.
    **
    ** Return the number of elements evaluated.
    */

        public static int ExprCodeExprList(
            Parse pParse, /* Parsing context */
            ExprList pList, /* The expression list to be coded */
            int target, /* Where to write results */
            bool doHardCopy /* Make a hard copy of every element */
            )
        {
            ExprList_item pItem;
            int i, n;
            Debug.Assert(pList != null);
            Debug.Assert(target > 0);
            n = pList.nExpr;
            for (i = 0; i < n; i++) // pItem++)
            {
                pItem = pList.a[i];
                if (pItem.iAlias != 0)
                {
                    int iReg = codeAlias(pParse, pItem.iAlias, pItem.pExpr, target + i);
                    Vdbe v = SelectHelper.GetVdbe(pParse);
                    if (iReg != target + i)
                    {
                        VdbeAux.VdbeAddOp2(v, OPCode.OP_SCopy, iReg, target + i);
                    }
                }
                else
                {
                    ExprCode(pParse, pItem.pExpr, target + i);
                }
                if (doHardCopy /* && 0 == pParse.db.mallocFailed */)
                {
                    ExprHardCopy(pParse, target, n);
                }
            }
            return n;
        }


        /*
    ** Generate code for a BETWEEN operator.
    **
    **    x BETWEEN y AND z
    **
    ** The above is equivalent to 
    **
    **    x>=y AND x<=z
    **
    ** Code it as such, taking care to do the common subexpression
    ** elementation of x.
    */

        private static void exprCodeBetween(
            Parse pParse, /* Parsing and code generating context */
            Expr pExpr, /* The BETWEEN expression */
            int dest, /* Jump here if the jump is taken */
            int jumpIfTrue, /* Take the jump if the BETWEEN is true */
            int jumpIfNull /* Take the jump if the BETWEEN is NULL */
            )
        {
            var exprAnd = new Expr(); /* The AND operator in  x>=y AND x<=z  */
            var compLeft = new Expr(); /* The  x>=y  term */
            var compRight = new Expr(); /* The  x<=z  term */
            Expr exprX; /* The  x  subexpression */
            int regFree1 = 0; /* Temporary use register */

            Debug.Assert(!Helper.ExprHasProperty(pExpr, ExprFlag.EP_xIsSelect));
            exprX = pExpr.pLeft.Copy();
            exprAnd.op = TokenKeyword.TK_AND;
            exprAnd.pLeft = compLeft;
            exprAnd.pRight = compRight;
            compLeft.op = TokenKeyword.TK_GE;
            compLeft.pLeft = exprX;
            compLeft.pRight = pExpr.x.pList.a[0].pExpr;
            compRight.op = TokenKeyword.TK_LE;
            compRight.pLeft = exprX;
            compRight.pRight = pExpr.x.pList.a[1].pExpr;
            exprX.iTable = ExprCodeTemp(pParse, exprX, ref regFree1);
            exprX.op = TokenKeyword.TK_REGISTER;
            if (jumpIfTrue != 0)
            {
                ExprIfTrue(pParse, exprAnd, dest, jumpIfNull);
            }
            else
            {
                ExprIfFalse(pParse, exprAnd, dest, jumpIfNull);
            }
            ReleaseTempReg(pParse, regFree1);

            /* Ensure adequate test coverage */
            UnitTest.TestCase(jumpIfTrue == 0 && jumpIfNull == 0 && regFree1 == 0);
            UnitTest.TestCase(jumpIfTrue == 0 && jumpIfNull == 0 && regFree1 != 0);
            UnitTest.TestCase(jumpIfTrue == 0 && jumpIfNull != 0 && regFree1 == 0);
            UnitTest.TestCase(jumpIfTrue == 0 && jumpIfNull != 0 && regFree1 != 0);
            UnitTest.TestCase(jumpIfTrue != 0 && jumpIfNull == 0 && regFree1 == 0);
            UnitTest.TestCase(jumpIfTrue != 0 && jumpIfNull == 0 && regFree1 != 0);
            UnitTest.TestCase(jumpIfTrue != 0 && jumpIfNull != 0 && regFree1 == 0);
            UnitTest.TestCase(jumpIfTrue != 0 && jumpIfNull != 0 && regFree1 != 0);
        }

        /*
    ** Generate code for a boolean expression such that a jump is made
    ** to the label "dest" if the expression is true but execution
    ** continues straight thru if the expression is false.
    **
    ** If the expression evaluates to NULL (neither true nor false), then
    ** take the jump if the jumpIfNull flag is Const.SQLITE_JUMPIFNULL.
    **
    ** This code depends on the fact that certain token values (ex: TokenKeyword.TK_EQ)
    ** are the same as opcode values (ex: OPCode.OP_Eq) that implement the corresponding
    ** operation.  Special comments in vdbe.c and the mkopcodeh.awk script in
    ** the make process cause these values to align.  Assert()s in the code
    ** below verify that the numbers are aligned correctly.
    */

        public static void ExprIfTrue(Parse pParse, Expr pExpr, int dest, int jumpIfNull)
        {
            Vdbe v = pParse.pVdbe;
            int op = 0;
            int regFree1 = 0;
            int regFree2 = 0;
            int r1 = 0, r2 = 0;

            Debug.Assert(jumpIfNull == Const.SQLITE_JUMPIFNULL || jumpIfNull == 0);
            if (UnitTest.NEVER(v == null)) return; /* Existance of VDBE checked by caller */
            if (UnitTest.NEVER(pExpr == null)) return; /* No way this can happen */
            op = pExpr.op;
            switch (op)
            {
                case TokenKeyword.TK_AND:
                    {
                        int d2 = VdbeAux.VdbeMakeLabel(v);
                        UnitTest.TestCase(jumpIfNull == 0);
                        ExprCachePush(pParse);
                        ExprIfFalse(pParse, pExpr.pLeft, d2, jumpIfNull ^ Const.SQLITE_JUMPIFNULL);
                        ExprIfTrue(pParse, pExpr.pRight, dest, jumpIfNull);
                        VdbeAux.VdbeResolveLabel(v, d2);
                        ExprCachePop(pParse, 1);
                        break;
                    }
                case TokenKeyword.TK_OR:
                    {
                        UnitTest.TestCase(jumpIfNull == 0);
                        ExprIfTrue(pParse, pExpr.pLeft, dest, jumpIfNull);
                        ExprIfTrue(pParse, pExpr.pRight, dest, jumpIfNull);
                        break;
                    }
                case TokenKeyword.TK_NOT:
                    {
                        UnitTest.TestCase(jumpIfNull == 0);
                        ExprIfFalse(pParse, pExpr.pLeft, dest, jumpIfNull);
                        break;
                    }
                case TokenKeyword.TK_LT:
                case TokenKeyword.TK_LE:
                case TokenKeyword.TK_GT:
                case TokenKeyword.TK_GE:
                case TokenKeyword.TK_NE:
                case TokenKeyword.TK_EQ:
                    {
                        Debug.Assert(TokenKeyword.TK_LT == OPCode.OP_Lt);
                        Debug.Assert(TokenKeyword.TK_LE == OPCode.OP_Le);
                        Debug.Assert(TokenKeyword.TK_GT == OPCode.OP_Gt);
                        Debug.Assert(TokenKeyword.TK_GE == OPCode.OP_Ge);
                        Debug.Assert(TokenKeyword.TK_EQ == OPCode.OP_Eq);
                        Debug.Assert(TokenKeyword.TK_NE == OPCode.OP_Ne);
                        UnitTest.TestCase(op == TokenKeyword.TK_LT);
                        UnitTest.TestCase(op == TokenKeyword.TK_LE);
                        UnitTest.TestCase(op == TokenKeyword.TK_GT);
                        UnitTest.TestCase(op == TokenKeyword.TK_GE);
                        UnitTest.TestCase(op == TokenKeyword.TK_EQ);
                        UnitTest.TestCase(op == TokenKeyword.TK_NE);
                        UnitTest.TestCase(jumpIfNull == 0);
                        r1 = ExprCodeTemp(pParse, pExpr.pLeft, ref regFree1);
                        r2 = ExprCodeTemp(pParse, pExpr.pRight, ref regFree2);
                        codeCompare(pParse, pExpr.pLeft, pExpr.pRight, op,
                                    r1, r2, dest, jumpIfNull);
                        UnitTest.TestCase(regFree1 == 0);
                        UnitTest.TestCase(regFree2 == 0);
                        break;
                    }
                case TokenKeyword.TK_IS:
                case TokenKeyword.TK_ISNOT:
                    {
                        UnitTest.TestCase(op == TokenKeyword.TK_IS);
                        UnitTest.TestCase(op == TokenKeyword.TK_ISNOT);
                        r1 = ExprCodeTemp(pParse, pExpr.pLeft, ref regFree1);
                        r2 = ExprCodeTemp(pParse, pExpr.pRight, ref regFree2);
                        op = (op == TokenKeyword.TK_IS) ? TokenKeyword.TK_EQ : TokenKeyword.TK_NE;
                        codeCompare(pParse, pExpr.pLeft, pExpr.pRight, op,
                                    r1, r2, dest, Const.SQLITE_NULLEQ);
                        UnitTest.TestCase(regFree1 == 0);
                        UnitTest.TestCase(regFree2 == 0);
                        break;
                    }
                case TokenKeyword.TK_ISNULL:
                case TokenKeyword.TK_NOTNULL:
                    {
                        Debug.Assert(TokenKeyword.TK_ISNULL == OPCode.OP_IsNull);
                        Debug.Assert(TokenKeyword.TK_NOTNULL == OPCode.OP_NotNull);
                        UnitTest.TestCase(op == TokenKeyword.TK_ISNULL);
                        UnitTest.TestCase(op == TokenKeyword.TK_NOTNULL);
                        r1 = ExprCodeTemp(pParse, pExpr.pLeft, ref regFree1);
                        VdbeAux.VdbeAddOp2(v, op, r1, dest);
                        UnitTest.TestCase(regFree1 == 0);
                        break;
                    }
                case TokenKeyword.TK_BETWEEN:
                    {
                        UnitTest.TestCase(jumpIfNull == 0);
                        exprCodeBetween(pParse, pExpr, dest, 1, jumpIfNull);
                        break;
                    }
                case TokenKeyword.TK_IN:
                    {
                        int destIfFalse = VdbeAux.VdbeMakeLabel(v);
                        int destIfNull = jumpIfNull != 0 ? dest : destIfFalse;
                        ExprCodeIN(pParse, pExpr, destIfFalse, destIfNull);
                        VdbeAux.VdbeAddOp2(v, OPCode.OP_Goto, 0, dest);
                        VdbeAux.VdbeResolveLabel(v, destIfFalse);
                        break;
                    }
                default:
                    {
                        r1 = ExprCodeTemp(pParse, pExpr, ref regFree1);
                        VdbeAux.VdbeAddOp3(v, OPCode.OP_If, r1, dest, jumpIfNull != 0 ? 1 : 0);
                        UnitTest.TestCase(regFree1 == 0);
                        UnitTest.TestCase(jumpIfNull == 0);
                        break;
                    }
            }
            ReleaseTempReg(pParse, regFree1);
            ReleaseTempReg(pParse, regFree2);
        }

        /*
    ** Generate code for a boolean expression such that a jump is made
    ** to the label "dest" if the expression is false but execution
    ** continues straight thru if the expression is true.
    **
    ** If the expression evaluates to NULL (neither true nor false) then
    ** jump if jumpIfNull is Const.SQLITE_JUMPIFNULL or fall through if jumpIfNull
    ** is 0.
    */

        public static void ExprIfFalse(Parse pParse, Expr pExpr, int dest, int jumpIfNull)
        {
            Vdbe v = pParse.pVdbe;
            int op = 0;
            int regFree1 = 0;
            int regFree2 = 0;
            int r1 = 0, r2 = 0;

            Debug.Assert(jumpIfNull == Const.SQLITE_JUMPIFNULL || jumpIfNull == 0);
            if (UnitTest.NEVER(v == null)) return; /* Existance of VDBE checked by caller */
            if (pExpr == null) return;

            /* The value of pExpr.op and op are related as follows:
      **
      **       pExpr.op            op
      **       ---------          ----------
      **       TokenKeyword.TK_ISNULL          OPCode.OP_NotNull
      **       TokenKeyword.TK_NOTNULL         OPCode.OP_IsNull
      **       TokenKeyword.TK_NE              OPCode.OP_Eq
      **       TokenKeyword.TK_EQ              OPCode.OP_Ne
      **       TokenKeyword.TK_GT              OPCode.OP_Le
      **       TokenKeyword.TK_LE              OPCode.OP_Gt
      **       TokenKeyword.TK_GE              OPCode.OP_Lt
      **       TokenKeyword.TK_LT              OPCode.OP_Ge
      **
      ** For other values of pExpr.op, op is undefined and unused.
      ** The value of TokenKeyword.TK_ and OPCode.OP_ constants are arranged such that we
      ** can compute the mapping above using the following expression.
      ** Assert()s verify that the computation is correct.
      */
            op = ((pExpr.op + (TokenKeyword.TK_ISNULL & 1)) ^ 1) - (TokenKeyword.TK_ISNULL & 1);

            /* Verify correct alignment of TokenKeyword.TK_ and OPCode.OP_ constants
      */
            Debug.Assert(pExpr.op != TokenKeyword.TK_ISNULL || op == OPCode.OP_NotNull);
            Debug.Assert(pExpr.op != TokenKeyword.TK_NOTNULL || op == OPCode.OP_IsNull);
            Debug.Assert(pExpr.op != TokenKeyword.TK_NE || op == OPCode.OP_Eq);
            Debug.Assert(pExpr.op != TokenKeyword.TK_EQ || op == OPCode.OP_Ne);
            Debug.Assert(pExpr.op != TokenKeyword.TK_LT || op == OPCode.OP_Ge);
            Debug.Assert(pExpr.op != TokenKeyword.TK_LE || op == OPCode.OP_Gt);
            Debug.Assert(pExpr.op != TokenKeyword.TK_GT || op == OPCode.OP_Le);
            Debug.Assert(pExpr.op != TokenKeyword.TK_GE || op == OPCode.OP_Lt);

            switch (pExpr.op)
            {
                case TokenKeyword.TK_AND:
                    {
                        UnitTest.TestCase(jumpIfNull == 0);
                        ExprIfFalse(pParse, pExpr.pLeft, dest, jumpIfNull);
                        ExprIfFalse(pParse, pExpr.pRight, dest, jumpIfNull);
                        break;
                    }
                case TokenKeyword.TK_OR:
                    {
                        int d2 = VdbeAux.VdbeMakeLabel(v);
                        UnitTest.TestCase(jumpIfNull == 0);
                        ExprCachePush(pParse);
                        ExprIfTrue(pParse, pExpr.pLeft, d2, jumpIfNull ^ Const.SQLITE_JUMPIFNULL);
                        ExprIfFalse(pParse, pExpr.pRight, dest, jumpIfNull);
                        VdbeAux.VdbeResolveLabel(v, d2);
                        ExprCachePop(pParse, 1);
                        break;
                    }
                case TokenKeyword.TK_NOT:
                    {
                        UnitTest.TestCase(jumpIfNull == 0);
                        ExprIfTrue(pParse, pExpr.pLeft, dest, jumpIfNull);
                        break;
                    }
                case TokenKeyword.TK_LT:
                case TokenKeyword.TK_LE:
                case TokenKeyword.TK_GT:
                case TokenKeyword.TK_GE:
                case TokenKeyword.TK_NE:
                case TokenKeyword.TK_EQ:
                    {
                        UnitTest.TestCase(op == TokenKeyword.TK_LT);
                        UnitTest.TestCase(op == TokenKeyword.TK_LE);
                        UnitTest.TestCase(op == TokenKeyword.TK_GT);
                        UnitTest.TestCase(op == TokenKeyword.TK_GE);
                        UnitTest.TestCase(op == TokenKeyword.TK_EQ);
                        UnitTest.TestCase(op == TokenKeyword.TK_NE);
                        UnitTest.TestCase(jumpIfNull == 0);
                        r1 = ExprCodeTemp(pParse, pExpr.pLeft, ref regFree1);
                        r2 = ExprCodeTemp(pParse, pExpr.pRight, ref regFree2);
                        codeCompare(pParse, pExpr.pLeft, pExpr.pRight, op,
                                    r1, r2, dest, jumpIfNull);
                        UnitTest.TestCase(regFree1 == 0);
                        UnitTest.TestCase(regFree2 == 0);
                        break;
                    }
                case TokenKeyword.TK_IS:
                case TokenKeyword.TK_ISNOT:
                    {
                        UnitTest.TestCase(pExpr.op == TokenKeyword.TK_IS);
                        UnitTest.TestCase(pExpr.op == TokenKeyword.TK_ISNOT);
                        r1 = ExprCodeTemp(pParse, pExpr.pLeft, ref regFree1);
                        r2 = ExprCodeTemp(pParse, pExpr.pRight, ref regFree2);
                        op = (pExpr.op == TokenKeyword.TK_IS) ? TokenKeyword.TK_NE : TokenKeyword.TK_EQ;
                        codeCompare(pParse, pExpr.pLeft, pExpr.pRight, op,
                                    r1, r2, dest, Const.SQLITE_NULLEQ);
                        UnitTest.TestCase(regFree1 == 0);
                        UnitTest.TestCase(regFree2 == 0);
                        break;
                    }
                case TokenKeyword.TK_ISNULL:
                case TokenKeyword.TK_NOTNULL:
                    {
                        UnitTest.TestCase(op == TokenKeyword.TK_ISNULL);
                        UnitTest.TestCase(op == TokenKeyword.TK_NOTNULL);
                        r1 = ExprCodeTemp(pParse, pExpr.pLeft, ref regFree1);
                        VdbeAux.VdbeAddOp2(v, op, r1, dest);
                        UnitTest.TestCase(regFree1 == 0);
                        break;
                    }
                case TokenKeyword.TK_BETWEEN:
                    {
                        UnitTest.TestCase(jumpIfNull == 0);
                        exprCodeBetween(pParse, pExpr, dest, 0, jumpIfNull);
                        break;
                    }
                case TokenKeyword.TK_IN:
                    {
                        if (jumpIfNull != 0)
                        {
                            ExprCodeIN(pParse, pExpr, dest, dest);
                        }
                        else
                        {
                            int destIfNull = VdbeAux.VdbeMakeLabel(v);
                            ExprCodeIN(pParse, pExpr, dest, destIfNull);
                            VdbeAux.VdbeResolveLabel(v, destIfNull);
                        }
                        break;
                    }
                default:
                    {
                        r1 = ExprCodeTemp(pParse, pExpr, ref regFree1);
                        VdbeAux.VdbeAddOp3(v, OPCode.OP_IfNot, r1, dest, jumpIfNull != 0 ? 1 : 0);
                        UnitTest.TestCase(regFree1 == 0);
                        UnitTest.TestCase(jumpIfNull == 0);
                        break;
                    }
            }
            ReleaseTempReg(pParse, regFree1);
            ReleaseTempReg(pParse, regFree2);
        }

        /*
    ** Do a deep comparison of two expression trees.  Return 0 if the two
    ** expressions are completely identical.  Return 1 if they differ only
    ** by a COLLATE operator at the top level.  Return 2 if there are differences
    ** other than the top-level COLLATE operator.
    **
    ** Sometimes this routine will return 2 even if the two expressions
    ** really are equivalent.  If we cannot prove that the expressions are
    ** identical, we return 2 just to be safe.  So if this routine
    ** returns 2, then you do not really know for certain if the two
    ** expressions are the same.  But if you get a 0 or 1 return, then you
    ** can be sure the expressions are the same.  In the places where
    ** this routine is used, it does not hurt to get an extra 2 - that
    ** just might result in some slightly slower code.  But returning
    ** an incorrect 0 or 1 could lead to a malfunction.
    */

        public static int ExprCompare(Expr pA, Expr pB)
        {
            int i;
            if (pA == null || pB == null)
            {
                return pB == pA ? 0 : 2;
            }
            Debug.Assert(!Helper.ExprHasAnyProperty(pA, ExprFlag.EP_TokenOnly | ExprFlag.EP_Reduced));
            Debug.Assert(!Helper.ExprHasAnyProperty(pB, ExprFlag.EP_TokenOnly | ExprFlag.EP_Reduced));
            if (Helper.ExprHasProperty(pA, ExprFlag.EP_xIsSelect) || Helper.ExprHasProperty(pB, ExprFlag.EP_xIsSelect))
            {
                return 2;
            }
            if ((pA.flags & ExprFlag.EP_Distinct) != (pB.flags & ExprFlag.EP_Distinct)) return 2;
            if (pA.op != pB.op) return 2;
            if (ExprCompare(pA.pLeft, pB.pLeft) != 0) return 2;
            if (ExprCompare(pA.pRight, pB.pRight) != 0) return 2;
            if (pA.x.pList != null && pB.x.pList != null)
            {
                if (pA.x.pList.nExpr != pB.x.pList.nExpr) return 2;
                for (i = 0; i < pA.x.pList.nExpr; i++)
                {
                    Expr pExprA = pA.x.pList.a[i].pExpr;
                    Expr pExprB = pB.x.pList.a[i].pExpr;
                    if (ExprCompare(pExprA, pExprB) != 0) return 2;
                }
            }
            else if (pA.x.pList != null || pB.x.pList != null)
            {
                return 2;
            }
            if (pA.iTable != pB.iTable || pA.iColumn != pB.iColumn) return 2;
            if (Helper.ExprHasProperty(pA, ExprFlag.EP_IntValue))
            {
                if (!Helper.ExprHasProperty(pB, ExprFlag.EP_IntValue) || pA.u.iValue != pB.u.iValue)
                {
                    return 2;
                }
            }
            else if (pA.op != TokenKeyword.TK_COLUMN && pA.u.zToken != null)
            {
                if (Helper.ExprHasProperty(pB, ExprFlag.EP_IntValue) || UnitTest.NEVER(pB.u.zToken == null)) return 2;
                if (Utility.Sqlite3StrICmp(pA.u.zToken, pB.u.zToken) != 0)
                {
                    return 2;
                }
            }
            if ((pA.flags & ExprFlag.EP_ExpCollate) != (pB.flags & ExprFlag.EP_ExpCollate)) return 1;
            if ((pA.flags & ExprFlag.EP_ExpCollate) != 0 && pA.pColl != pB.pColl) return 2;
            return 0;
        }


        /*
    ** Add a new element to the pAggInfo.aCol[] array.  Return the index of
    ** the new element.  Return a negative number if malloc fails.
    */

        private static int addAggInfoColumn(sqlite3 db, AggInfo pInfo)
        {
            int i = 0;
            pInfo.aCol =Build. ArrayAllocate(
                db,
                pInfo.aCol,
                -1, //sizeof(pInfo.aCol[0]),
                3,
                ref pInfo.nColumn,
                ref pInfo.nColumnAlloc,
                ref i
                );
            return i;
        }

        /*
    ** Add a new element to the pAggInfo.aFunc[] array.  Return the index of
    ** the new element.  Return a negative number if malloc fails.
    */

        private static int addAggInfoFunc(sqlite3 db, AggInfo pInfo)
        {
            int i = 0;
            pInfo.aFunc =Build. ArrayAllocate(
                db,
                pInfo.aFunc,
                -1, //sizeof(pInfo.aFunc[0]),
                3,
                ref pInfo.nFunc,
                ref pInfo.nFuncAlloc,
                ref i
                );
            return i;
        }

        /*
    ** This is the xExprCallback for a tree walker.  It is used to
    ** implement ExprHelper.ExprAnalyzeAggregates().  See ExprHelper.ExprAnalyzeAggregates
    ** for additional information.
    */

        private static int analyzeAggregate(Walker pWalker, ref Expr pExpr)
        {
            int i;
            NameContext pNC = pWalker.u.pNC;
            Parse pParse = pNC.pParse;
            SrcList pSrcList = pNC.pSrcList;
            AggInfo pAggInfo = pNC.pAggInfo;

            switch (pExpr.op)
            {
                case TokenKeyword.TK_AGG_COLUMN:
                case TokenKeyword.TK_COLUMN:
                    {
                        UnitTest.TestCase(pExpr.op == TokenKeyword.TK_AGG_COLUMN);
                        UnitTest.TestCase(pExpr.op == TokenKeyword.TK_COLUMN);
                        /* Check to see if the column is in one of the tables in the FROM
            ** clause of the aggregate query */
                        if (UnitTest.ALWAYS(pSrcList != null))
                        {
                            SrcList_item pItem; // = pSrcList.a;
                            for (i = 0; i < pSrcList.nSrc; i++)
                            {
//, pItem++){
                                pItem = pSrcList.a[i];
                                AggInfo_col pCol;
                                Debug.Assert(!Helper.ExprHasAnyProperty(pExpr, ExprFlag.EP_TokenOnly | ExprFlag.EP_Reduced));
                                if (pExpr.iTable == pItem.iCursor)
                                {
                                    /* If we reach this point, it means that pExpr refers to a table
                  ** that is in the FROM clause of the aggregate query.
                  **
                  ** Make an entry for the column in pAggInfo.aCol[] if there
                  ** is not an entry there already.
                  */
                                    int k;
                                    //pCol = pAggInfo.aCol;
                                    for (k = 0; k < pAggInfo.nColumn; k++)
                                    {
//, pCol++){
                                        pCol = pAggInfo.aCol[k];
                                        if (pCol.iTable == pExpr.iTable &&
                                            pCol.iColumn == pExpr.iColumn)
                                        {
                                            break;
                                        }
                                    }
                                    if ((k >= pAggInfo.nColumn)
                                        && (k = addAggInfoColumn(pParse.db, pAggInfo)) >= 0
                                        )
                                    {
                                        pCol = pAggInfo.aCol[k];
                                        pCol.pTab = pExpr.pTab;
                                        pCol.iTable = pExpr.iTable;
                                        pCol.iColumn = pExpr.iColumn;
                                        pCol.iMem = ++pParse.nMem;
                                        pCol.iSorterColumn = -1;
                                        pCol.pExpr = pExpr;
                                        if (pAggInfo.pGroupBy != null)
                                        {
                                            int j, n;
                                            ExprList pGB = pAggInfo.pGroupBy;
                                            ExprList_item pTerm; // = pGB.a;
                                            n = pGB.nExpr;
                                            for (j = 0; j < n; j++)
                                            {
//, pTerm++){
                                                pTerm = pGB.a[j];
                                                Expr pE = pTerm.pExpr;
                                                if (pE.op == TokenKeyword.TK_COLUMN && pE.iTable == pExpr.iTable &&
                                                    pE.iColumn == pExpr.iColumn)
                                                {
                                                    pCol.iSorterColumn = j;
                                                    break;
                                                }
                                            }
                                        }
                                        if (pCol.iSorterColumn < 0)
                                        {
                                            pCol.iSorterColumn = pAggInfo.nSortingColumn++;
                                        }
                                    }
                                    /* There is now an entry for pExpr in pAggInfo.aCol[] (either
                  ** because it was there before or because we just created it).
                  ** Convert the pExpr to be a TokenKeyword.TK_AGG_COLUMN referring to that
                  ** pAggInfo.aCol[] entry.
                  */
                                    Expr.ExprSetIrreducible(pExpr);
                                    pExpr.pAggInfo = pAggInfo;
                                    pExpr.op = TokenKeyword.TK_AGG_COLUMN;
                                    pExpr.iAgg = (short) k;
                                    break;
                                } /* endif pExpr.iTable==pItem.iCursor */
                            } /* end loop over pSrcList */
                        }
                        return Wrc.WRC_Prune;
                    }
                case TokenKeyword.TK_AGG_FUNCTION:
                    {
                        /* The pNC.nDepth==0 test causes aggregate functions in subqueries
            ** to be ignored */
                        if (pNC.nDepth == 0)
                        {
                            /* Check to see if pExpr is a duplicate of another aggregate
              ** function that is already in the pAggInfo structure
              */
                            AggInfo_func pItem; // = pAggInfo.aFunc;
                            for (i = 0; i < pAggInfo.nFunc; i++)
                            {
//, pItem++){
                                pItem = pAggInfo.aFunc[i];
                                if (ExprCompare(pItem.pExpr, pExpr) == 0)
                                {
                                    break;
                                }
                            }
                            if (i >= pAggInfo.nFunc)
                            {
                                /* pExpr is original.  Make a new entry in pAggInfo.aFunc[]
                */
                                byte enc = pParse.db.aDbStatic[0].pSchema.enc; // Helper.ENC(pParse.db);
                                i = addAggInfoFunc(pParse.db, pAggInfo);
                                if (i >= 0)
                                {
                                    Debug.Assert(!Helper.ExprHasProperty(pExpr, ExprFlag.EP_xIsSelect));
                                    pItem = pAggInfo.aFunc[i];
                                    pItem.pExpr = pExpr;
                                    pItem.iMem = ++pParse.nMem;
                                    Debug.Assert(!Helper.ExprHasProperty(pExpr, ExprFlag.EP_IntValue));
                                    pItem.pFunc = Callback.FindFunction(pParse.db,
                                                                      pExpr.u.zToken, Utility.Sqlite3Strlen30(pExpr.u.zToken),
                                                                      pExpr.x.pList != null ? pExpr.x.pList.nExpr : 0,
                                                                      enc, 0);
                                    if ((pExpr.flags & ExprFlag.EP_Distinct) != 0)
                                    {
                                        pItem.iDistinct = pParse.nTab++;
                                    }
                                    else
                                    {
                                        pItem.iDistinct = -1;
                                    }
                                }
                            }
                            /* Make pExpr point to the appropriate pAggInfo.aFunc[] entry
              */
                            Debug.Assert(!Helper.ExprHasAnyProperty(pExpr, ExprFlag.EP_TokenOnly | ExprFlag.EP_Reduced));
                            Expr.ExprSetIrreducible(pExpr);
                            pExpr.iAgg = (short) i;
                            pExpr.pAggInfo = pAggInfo;
                            return Wrc.WRC_Prune;
                        }
                        break;
                    }
            }
            return Wrc.WRC_Continue;
        }

        private static int analyzeAggregatesInSelect(Walker pWalker, Select pSelect)
        {
            NameContext pNC = pWalker.u.pNC;
            if (pNC.nDepth == 0)
            {
                pNC.nDepth++;
                sqlite3WalkSelect(pWalker, pSelect);
                pNC.nDepth--;
                return Wrc.WRC_Prune;
            }
            else
            {
                return Wrc.WRC_Continue;
            }
        }


        /*
    ** Analyze the given expression looking for aggregate functions and
    ** for variables that need to be added to the pParse.aAgg[] array.
    ** Make additional entries to the pParse.aAgg[] array as necessary.
    **
    ** This routine should only be called after the expression has been
    ** analyzed by sqlite3ResolveExprNames().
    */

        public static void ExprAnalyzeAggregates(NameContext pNC, ref Expr pExpr)
        {
            var w = new Walker();
            w.xExprCallback = analyzeAggregate;
            w.xSelectCallback = analyzeAggregatesInSelect;
            w.u.pNC = pNC;
            Debug.Assert(pNC.pSrcList != null);
            sqlite3WalkExpr(w, ref pExpr);
        }

        /*
    ** Call ExprHelper.ExprAnalyzeAggregates() for every expression in an
    ** expression list.  Return the number of errors.
    **
    ** If an error is found, the analysis is cut short.
    */

        public static void ExprAnalyzeAggList(NameContext pNC, ExprList pList)
        {
            ExprList_item pItem;
            int i;
            if (pList != null)
            {
                for (i = 0; i < pList.nExpr; i++) //, pItem++)
                {
                    pItem = pList.a[i];
                    ExprAnalyzeAggregates(pNC, ref pItem.pExpr);
                }
            }
        }

        /*
    ** Allocate a single new register for use to hold some intermediate result.
    */

        public static int GetTempReg(Parse pParse)
        {
            if (pParse.nTempReg == 0)
            {
                return ++pParse.nMem;
            }
            return pParse.aTempReg[--pParse.nTempReg];
        }

        /*
    ** Deallocate a register, making available for reuse for some other
    ** purpose.
    **
    ** If a register is currently being used by the column cache, then
    ** the dallocation is deferred until the column cache line that uses
    ** the register becomes stale.
    */

        public static void ReleaseTempReg(Parse pParse, int iReg)
        {
            if (iReg != 0 && pParse.nTempReg < Utility.ArraySize(pParse.aTempReg))
            {
                int i;
                yColCache p;
                for (i = 0; i < Const.SQLITE_N_COLCACHE; i++) //p=pParse.aColCache... p++)
                {
                    p = pParse.aColCache[i];
                    if (p.iReg == iReg)
                    {
                        p.tempReg = 1;
                        return;
                    }
                }
                pParse.aTempReg[pParse.nTempReg++] = iReg;
            }
        }

        /*
    ** Allocate or deallocate a block of nReg consecutive registers
    */

        public static int GetTempRange(Parse pParse, int nReg)
        {
            int i, n;
            i = pParse.iRangeReg;
            n = pParse.nRangeReg;
            if (nReg <= n)
            {
                Debug.Assert(0 == usedAsColumnCache(pParse, i, i + n - 1));
                pParse.iRangeReg += nReg;
                pParse.nRangeReg -= nReg;
            }
            else
            {
                i = pParse.nMem + 1;
                pParse.nMem += nReg;
            }
            return i;
        }

        public static void ReleaseTempRange(Parse pParse, int iReg, int nReg)
        {
            ExprCacheRemove(pParse, iReg, nReg);
            if (nReg > pParse.nRangeReg)
            {
                pParse.nRangeReg = nReg;
                pParse.iRangeReg = iReg;
            }
        }
    }
}